package com.wzdigit.wms.wms.service.outbound;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.wzdigit.framework.Shift;
import com.wzdigit.mes.basic.api.*;
import com.wzdigit.mes.basic.dto.CustomerDto;
import com.wzdigit.mes.basic.dto.DataItemDetailDTO;
import com.wzdigit.mes.basic.dto.DataitemAttrDto;
import com.wzdigit.wms.basic.annotation.FormLock;
import com.wzdigit.wms.basic.client.Pager;
import com.wzdigit.wms.basic.client.PagerInfo;
import com.wzdigit.wms.basic.client.Queryer;
import com.wzdigit.wms.basic.client.request.picktask.PicktaskMangementRequest;
import com.wzdigit.wms.basic.client.request.picktask.cancel.CancelPicktaskDtlRequest;
import com.wzdigit.wms.basic.client.request.picktask.cancel.CancelPicktaskRequest;
import com.wzdigit.wms.basic.client.request.transfer.SearchInvRequest;
import com.wzdigit.wms.basic.client.response.Result;
import com.wzdigit.wms.basic.client.response.basic.LocationResponse;
import com.wzdigit.wms.basic.client.response.basic.UserWhsZoneWorkDivisionResponse;
import com.wzdigit.wms.basic.client.response.picktask.*;
import com.wzdigit.wms.basic.client.response.picktask.cancel.PicktaskCancelResp;
import com.wzdigit.wms.basic.client.response.transfer.InvZoneDto;
import com.wzdigit.wms.basic.client.response.transfer.IssuePickingItemResponse;
import com.wzdigit.wms.basic.client.response.transfer.IssuePickingResponse;
import com.wzdigit.wms.basic.domain.basic.*;
import com.wzdigit.wms.basic.domain.dtos.basic.SkuDto;
import com.wzdigit.wms.basic.domain.dtos.inventory.GetUserWhsZoneWorkDivisionDto;
import com.wzdigit.wms.basic.domain.dtos.inventory.SnInforDto;
import com.wzdigit.wms.basic.domain.dtos.inventory.UpdateInventoryInputDto;
import com.wzdigit.wms.basic.domain.dtos.outbound.PicktaskDto;
import com.wzdigit.wms.basic.domain.dtos.transfer.GdnDtlDto;
import com.wzdigit.wms.basic.domain.inventory.Goods;
import com.wzdigit.wms.basic.domain.inventory.GoodsLog;
import com.wzdigit.wms.basic.domain.inventory.InvLoc;
import com.wzdigit.wms.basic.domain.transfer.*;
import com.wzdigit.wms.basic.enums.*;
import com.wzdigit.wms.basic.enums.log.GoodsLogEnum;
import com.wzdigit.wms.basic.enums.workdivision.WorkDivisionTypeEnum;
import com.wzdigit.wms.basic.mapper.core.basic.WarehouseMapper;
import com.wzdigit.wms.basic.mapper.core.basic.ZoneMapper;
import com.wzdigit.wms.basic.mapper.core.inventory.GoodsLogMapper;
import com.wzdigit.wms.basic.mapper.core.transfer.CtnrLoadingMapper;
import com.wzdigit.wms.basic.mapper.core.transfer.DocPreAlocMapper;
import com.wzdigit.wms.basic.mapper.core.transfer.GdnHeaderMapper;
import com.wzdigit.wms.basic.mapper.core.transfer.PicktaskHeaderMapper;
import com.wzdigit.wms.basic.service.code.InventoryService;
import com.wzdigit.wms.common.entity.CommonProperty;
import com.wzdigit.wms.basic.util.HttpSessionUtil;
import com.wzdigit.wms.common.entity.StatusCode;
import com.wzdigit.wms.wms.service.basic.*;
import com.wzdigit.wms.wms.service.core.*;
import com.wzdigit.wms.wms.service.core.ctrn.CtnrLoadingService;
import com.wzdigit.wms.wms.service.core.outbound.PicktaskDtlGoodsService;
import com.wzdigit.wms.wms.service.core.outbound.PicktaskDtlService;
import com.wzdigit.wms.wms.service.core.outbound.PicktaskHeaderService;
import com.wzdigit.wms.wms.service.core.transfer.DocPreAlocService;
import com.wzdigit.wms.wms.service.order.TransferService;
import com.wzdigit.wms.wms.service.transfer.InvZoneService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 拣货任务Service
 */
@Service
@Slf4j
public class PicktaskService {

    @Autowired
    PicktaskHeaderService picktaskHeaderService;

    @Autowired
    PicktaskHeaderMapper picktaskHeaderMapper;

    @Autowired
    PicktaskDtlService picktaskDtlService;

    @Autowired
    PicktaskDtlGoodsService picktaskDtlGoodsService;

    @Autowired
    ZoneService zoneService;

    @Autowired
    WarehouseService warehouseService;

    @Autowired
    GdnCoreService gdnCoreService;

    @Autowired
    SkuService skuService;

    @Autowired
    private RedissonClient redisson;

    @Autowired
    CtnrLoadingMapper ctnrLoadingMapper;

    @Autowired
    CtnrLoadingService ctnrLoadingService;

    @Autowired
    LocationService locationService;

    @Autowired
    WarehouseAttrService warehouseAttrService;

    @Autowired
    InvLocService invLocService;

    @Autowired
    GoodsService goodsService;

    @Autowired
    InvZoneService invZoneService;

    @Autowired
    WorkDivisionCoreService workDivisionCoreService;

    @Autowired
    DocPreAlocMapper docPreAlocMapper;

    @Autowired
    WarehouseMapper warehouseMapper;

    @Autowired
    ZoneMapper zoneMapper;

    @Autowired
    GoodsLogMapper goodsLogMapper;

    @Autowired
    DocPreAlocService docPreAlocService;

    @Autowired
    InventoryService inventoryService;

    @Autowired
    TransferService transferService;

    @Autowired
    GdnHeaderMapper gdnHeaderMapper;


    @Reference(version = "${dubbo.providers.basic.customer.api.version:1.0.0}", check = false)
    BasicGetCustomerApi basicGetCustomerApi;

    @Reference(version = "${dubbo.providers.basic.vendor.api.version:1.0.0}", check = false)
    BasicVendorApi basicVendorApi;

    @Reference(version = "${dubbo.consumers.basic.data.item.detail.api:1.0.0}", check = false, timeout = 300000)
    BasicDataItemDetailApi basicDataItemDetailApi;

    @Reference(version = "${dubbo.consumers.basic.data.attr.api:1.0.0}", check = false, timeout = 300000)
    BasicDataAttrApi basicDataAttrApi;

    @Reference(version = "${dubbo.consumers.basic.simple.code.rule.api:1.0.0}", check = false)
    BasicSimpleCodeRuleApi basicSimpleCodeRuleApi;

    @Value("${dataItem.warehouse_attr.dtl.picktask_key}")
    private String picktaskKey;

    @Value("${dataItem.warehouse_attr.dtl.pick_rule.data_name}")
    private String pickRuleKey;

    @Value("${dataItem.warehouse_attr.dtl.pick_rule.value}")
    private String pickRule;

    @Value("${dataItem.warehouse_attr.warehouse}")
    private String warehouseCode;

    @Value("${dataItem.orderType.gdn.code}")
    private String gdn;

    @Value("${dataItem.orderType.gdn.other_attr}")
    private String gdnAttr;

    @Value("${order_code.picktask.code}")
    private String picktaskCode;


    private static final Integer commonFlag = 1;//通用真值

    /**
     * 获取当前登录用户的拣货信息【单头】
     *
     * @param servletRequest
     * @param servletResponse
     * @param filter
     * @return
     */
    public Result<List<PicktaskDto>> getMainInfo(String filter, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        //登录用户名
        String loginName = HttpSessionUtil.getLoginName(servletRequest);
//        String loginName = "gxm";

        //查询条件式主档状态为【未拣货】和【拣货中】
        List<PicktaskDto> picktaskDtoList = getPicktaskDtos(filter, loginName, null);
        picktaskDtoList = picktaskDtoList.stream().filter(s -> {
            String taskReceiver = s.getTaskReceiver();
            List<String> receverList = Arrays.asList(taskReceiver.split(","));
            boolean flag = false;
            for (String recever : receverList) {
                if (recever.equals(loginName)) {
                    flag = true;
                }
            }
            return flag;
        }).collect(Collectors.toList());

        Map<Integer, List<PicktaskDto>> pickMap = picktaskDtoList.stream().collect(Collectors.groupingBy(PicktaskDto::getPicktaskHeaderId));


        List<PicktaskDto> picktaskDtoList1 = new ArrayList<>();

        //去重，设置任务数量和待检数量
        for (Map.Entry<Integer, List<PicktaskDto>> entry : pickMap.entrySet()) {
            List<PicktaskDto> picktaskDtos = entry.getValue();

            int planTotal = picktaskDtos.stream().filter(s -> s.getPlanQty() != null).mapToInt(PicktaskDto::getPlanQty).sum();
            int cmpTotal = picktaskDtos.stream().filter(s -> s.getCmpQty() != null).mapToInt(PicktaskDto::getCmpQty).sum();

            PicktaskDto picktaskDto = picktaskDtos.get(0);
            picktaskDto.setPlanQty(planTotal);
            picktaskDto.setCmpQty(cmpTotal);

            picktaskDtoList1.add(picktaskDto);
        }


        for (PicktaskDto picktaskDto : picktaskDtoList1) {
            //设置类型描述
            String typeEnumName = PicktaskTypeEnum.getEnumByCode(picktaskDto.getTaskType()).getEnumName();
            picktaskDto.setTaskTypeDesc(typeEnumName);

            //设置状态
            String statusEnumName = PicktaskStatusEnum.getEnumByCode(picktaskDto.getStatus()).getEnumName();
            picktaskDto.setStatusDesc(statusEnumName);

            //设置待检数量
            int waitCheckQty = picktaskDto.getPlanQty() - picktaskDto.getCmpQty();
            picktaskDto.setWaitCheckQty(waitCheckQty);
        }

        //加个排序，根据创建时间
//        Comparator comparator = ;};;

        Collections.sort(picktaskDtoList1, new Comparator<PicktaskDto>() {
            @Override
            public int compare(PicktaskDto o1, PicktaskDto o2) {
//                return Math.toIntExact(o2.getCreateTime().getTime() - o1.getCreateTime().getTime());

                return o2.compareTo(o1);
            }
        });

        Result<List<PicktaskDto>> result = new Result<>();
        result.setData(picktaskDtoList1);

        return result;
    }


    public PagerInfo<List<PicktaskDto>> getMainPage(Queryer<String> requestQueryer, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        //登录用户名
        String loginName = HttpSessionUtil.getLoginName(servletRequest);
        String filter = requestQueryer.getRequest();
        Pager<?> page = requestQueryer.getPage();

        // page为空，默认最大分页
        if (page == null) {
            page = new Pager<PicktaskMangementResp>();
            page.setSkipCount(0);
            page.setMaxResultCount(Integer.MAX_VALUE);
        }

        PagerInfo<List<PicktaskDto>> info = new PagerInfo<>();

        Page<PicktaskDto> _page = PageHelper.offsetPage(page.getSkipCount(), page.getMaxResultCount());
        PageInfo<PicktaskDto> result = new PageInfo<>(_page);

        //查询条件式主档状态为【未拣货】和【拣货中】
        //List<PicktaskDto> picktaskDtoList = getPicktaskDtos(filter, loginName, null);
        //查询未拣货和拣货中
        List<Integer> pickStatus = Arrays.asList(PicktaskStatusEnum.NO_PICK.getCode(), PicktaskStatusEnum.PICK_ING.getCode());
        List<PicktaskDto> picktaskDtoList = picktaskHeaderMapper.selectHeaderList(filter,loginName,pickStatus);



       /* picktaskDtoList = picktaskDtoList.stream().filter(s -> {
            String taskReceiver = s.getTaskReceiver();
            List<String> receverList = Arrays.asList(taskReceiver.split(","));
            boolean flag = false;
            for (String recever : receverList) {
                if (recever.equals(loginName)) {
                    flag = true;
                }
            }
            return flag;
        }).collect(Collectors.toList());*/

        Map<Integer, List<PicktaskDto>> pickMap = picktaskDtoList.stream().collect(Collectors.groupingBy(PicktaskDto::getPicktaskHeaderId));


        List<PicktaskDto> picktaskDtoList1 = new ArrayList<>();

      /*  //去重，设置任务数量和待检数量
        for (Map.Entry<Integer, List<PicktaskDto>> entry : pickMap.entrySet()) {
            List<PicktaskDto> picktaskDtos = entry.getValue();

            int planTotal = picktaskDtos.stream().filter(s -> s.getPlanQty() != null).mapToInt(PicktaskDto::getPlanQty).sum();
            int cmpTotal = picktaskDtos.stream().filter(s -> s.getCmpQty() != null).mapToInt(PicktaskDto::getCmpQty).sum();

            PicktaskDto picktaskDto = picktaskDtos.get(0);
            picktaskDto.setPlanQty(planTotal);
            picktaskDto.setCmpQty(cmpTotal);

            picktaskDtoList1.add(picktaskDto);
        }
*/

        for (PicktaskDto picktaskDto : picktaskDtoList) {
            //设置类型描述
            String typeEnumName = PicktaskTypeEnum.getEnumByCode(picktaskDto.getTaskType()).getEnumName();
            picktaskDto.setTaskTypeDesc(typeEnumName);

            //设置状态
            String statusEnumName = PicktaskStatusEnum.getEnumByCode(picktaskDto.getStatus()).getEnumName();
            picktaskDto.setStatusDesc(statusEnumName);

            //设置待检数量
            int waitCheckQty = picktaskDto.getPlanQty() - picktaskDto.getCmpQty();
            picktaskDto.setWaitCheckQty(waitCheckQty);
        }

        //加个排序，根据创建时间
//        Comparator comparator = ;};;

       /* Collections.sort(picktaskDtoList1, new Comparator<PicktaskDto>() {
            @Override
            public int compare(PicktaskDto o1, PicktaskDto o2) {

                return o2.compareTo(o1);
            }
        });*/

        result.setList(picktaskDtoList);
        info.setTotalCount(_page.getTotal());// 总数
        info.setItems(result.getList());// 返回结果

        return info;
    }


    /**
     * 根据登录用户和状态查询拣货信息
     *
     * @param filter
     * @param loginName
     * @return
     */
    private List<PicktaskDto> getPicktaskDtos(String filter, String loginName, Integer picktaskHeaderId) {
        List<Integer> allowStatusList = getPicktastStatus();

        return picktaskHeaderService.getMainAndDtlInfo(filter, loginName, allowStatusList, picktaskHeaderId);
    }

    /**
     * 获取主档允许查询的状态
     *
     * @return
     */
    private List<Integer> getPicktastStatus() {
        int noPickCode = PicktaskStatusEnum.NO_PICK.getCode();//未拣货
        int pickIngCode = PicktaskStatusEnum.PICK_ING.getCode();//拣货中

        List<Integer> allowStatusList = new ArrayList<>();
        allowStatusList.add(noPickCode);
        allowStatusList.add(pickIngCode);
        return allowStatusList;
    }

    /**
     * 获取档期那用户的拣货详细信息
     *
     * @param picktaskHeaderId
     * @param servletRequest
     * @return
     */
    public Result<PicktaskDtlInfoResp> getDetails(Integer picktaskHeaderId, HttpServletRequest servletRequest) {

        Result<PicktaskDtlInfoResp> result = new Result<>();
        //处理回参
        PicktaskDtlInfoResp picktaskDtlInfoResp = new PicktaskDtlInfoResp();


        String loginName = HttpSessionUtil.getLoginName(servletRequest);
//        String loginName = "gxm";
        //查询条件式主档状态为【未拣货】和【拣货中】
        List<Integer> picktastStatusList = this.getPicktastStatus();

        //先查HEADER
//        List<PicktaskHeader> picktaskHeader = picktaskHeaderService.getHeaderInfo(loginName, picktaskHeaderId, picktastStatusList);

        PicktaskHeader picktaskHeader = picktaskHeaderService.selectByHeadIdAndStatus(picktaskHeaderId, picktastStatusList);


        if (picktaskHeader == null) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "该单据状态在【拣货中】未找到记录");
        }

        //返回的HEADER
        PicktaskHeaderResp picktaskHeaderResp = new PicktaskHeaderResp();
        BeanUtils.copyProperties(picktaskHeader, picktaskHeaderResp);

        //设置仓库
        Warehouse warehouse = warehouseService.selectByPK(picktaskHeaderResp.getWhsId());
        picktaskHeaderResp.setWhsDesc(warehouse.getWhsName());

        //设置拣货类型
        String typeEnumName = PicktaskTypeEnum.getEnumByCode(picktaskHeaderResp.getTaskType()).getEnumName();
        picktaskHeaderResp.setTaskTypeDesc(typeEnumName);


        //设置库区
//        Zone zone = zoneService.getById(picktaskHeaderResp.getZoonId());
//        picktaskHeaderResp.setZoonDesc(zone.getZoneName());


        //任务详情
        //先查询所有header下的所有DTL
        List<PicktaskDtl> picktaskDtlList = picktaskDtlService.getDtlListByPicktaskHeaderId(loginName, picktaskHeaderId);

        if (picktaskDtlList.size() > 0) {
            if (picktaskDtlList.get(0).getZoneId() != null) {
                Zone zone = zoneService.getById(picktaskDtlList.get(0).getZoneId());
                picktaskHeaderResp.setZoonDesc(zone.getZoneName());
            }
        } else {
            picktaskDtlInfoResp.setPicktaskHeaderResp(picktaskHeaderResp);
            result.setData(picktaskDtlInfoResp);
            return result;
        }


        BigDecimal planTotal = picktaskDtlList.stream().filter(s -> s.getPlanQty() != null).map(PicktaskDtl::getPlanQty).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal cmpTotal = picktaskDtlList.stream().filter(s -> s.getCmpQty() != null).map(PicktaskDtl::getCmpQty).reduce(BigDecimal.ZERO, BigDecimal::add);
//        int planTotal = picktaskDtlList.stream().filter(s -> s.getPlanQty() != null).mapToInt(PicktaskDtl::getPlanQty).sum();
//        int cmpTotal = picktaskDtlList.stream().filter(s -> s.getCmpQty() != null).mapToInt(PicktaskDtl::getCmpQty).sum();

        //设置单头的任务数量和待检数量
        picktaskHeaderResp.setPlanQty(planTotal);
        picktaskHeaderResp.setCmpQty(cmpTotal);


        //TODO 详情出参
        List<PicktaskDtlResp> picktaskDtlRespList = new ArrayList<>();

        for (PicktaskDtl picktaskDtl : picktaskDtlList) {
            PicktaskDtlResp picktaskDtlResp = new PicktaskDtlResp();
            BeanUtils.copyProperties(picktaskDtl, picktaskDtlResp);

            picktaskDtlRespList.add(picktaskDtlResp);
        }

        //获取物料编码：获取发货单明细-》获取sku_id-》获取物料编码

        //发货单明细ID
        List<Long> docDtlIdList = picktaskDtlRespList.stream()
                .filter(s -> s.getDocDtlId() != null)
                .distinct()
                .map(PicktaskDtlResp::getDocDtlId)
                .collect(Collectors.toList());

        //根据发货单明细ID查询发货单信息
        List<GdnDtl> gdnDtlList = gdnCoreService.getGdnDtlListByPKList(docDtlIdList);

        List<Integer> skuIdList = gdnDtlList.stream().filter(s -> s.getSkuId() != null).map(s -> s.getSkuId()).collect(Collectors.toList());
        List<Sku> skuList = skuService.getSkuListBySkuIdList(skuIdList);


        List<GdnDtlDto> gdnDtlDtoList = new ArrayList<>();

        for (GdnDtl gdnDtl : gdnDtlList) {
            GdnDtlDto gdnDtlDto = new GdnDtlDto();
            BeanUtils.copyProperties(gdnDtl, gdnDtlDto);

            gdnDtlDtoList.add(gdnDtlDto);
        }


        //根据客户ID查客户信息
        List<Integer> ownerList = skuList.stream().distinct().filter(s -> s.getOwner() != null).map(s -> s.getOwner()).collect(Collectors.toList());
        //调BASIC接口查询客户信息
        List<CustomerDto> customerDtoList = basicGetCustomerApi.getCustomer(ownerList);

        List<SkuDto> skuDtoList = new ArrayList<>();
        for (Sku sku : skuList) {
            SkuDto skuDto = new SkuDto();
            BeanUtils.copyProperties(sku, skuDto);
            skuDtoList.add(skuDto);
        }


        for (SkuDto skuDto : skuDtoList) {
            for (CustomerDto customerDto : customerDtoList) {
                if (skuDto.getOwner() != null && skuDto.getOwner().equals(customerDto.getCustomerId())) {
                    //设置客户名
                    skuDto.setCustomerName(customerDto.getCustomerName());
                }
            }
        }


        //根据SKU_ID集合查询SKU集合信息

        for (GdnDtlDto gdnDtlDto : gdnDtlDtoList) {
            for (SkuDto sku : skuDtoList) {
                if (sku.getSkuId().equals(gdnDtlDto.getSkuId())) {
                    //设置物料编码
                    gdnDtlDto.setMaterialCode(sku.getSkuCode());
                    //设置客户名称
                    gdnDtlDto.setCustomName(sku.getCustomerName());
                    //物料描述
                    gdnDtlDto.setMaterialDesc(sku.getDescCn());
                }
            }
        }

        //根据发货单明细ID查询装柜单ID
        //创建类型的条件构造器
//        QueryWrapper<CtnrLoading> wrapper = new QueryWrapper<>();
//        wrapper.in("GDN_DTL_ID", docDtlIdList);
//        List<CtnrLoading> ctnrLoadingList = ctnrLoadingMapper.selectList(wrapper);
//
//        for (GdnDtlDto gdnDtlDto : gdnDtlDtoList) {
//            for (CtnrLoading ctnrLoading : ctnrLoadingList) {
//                if (ctnrLoading.getGdnDtlId().equals(gdnDtlDto.getGdnDtlId())) {
//                    gdnDtlDto.setRefSoLineNo(ctnrLoading.getRefCntrNo());
//                }
//            }
//        }


        for (PicktaskDtlResp picktaskDtlResp : picktaskDtlRespList) {
            for (GdnDtlDto gdnDtlDto : gdnDtlDtoList) {
                if (gdnDtlDto.getGdnDtlId().equals(picktaskDtlResp.getDocDtlId())) {
                    //设置行号
                    picktaskDtlResp.setLineNo(gdnDtlDto.getGdnLineNo());

                    //设置订单号
                    picktaskDtlResp.setPoNo(gdnDtlDto.getPoNo());

                    //设置客户型号
                    picktaskDtlResp.setModel(gdnDtlDto.getCustomerModle());

                    //设置物料code
                    picktaskDtlResp.setMaterialCode(gdnDtlDto.getMaterialCode());

                    //设置参考柜号
                    picktaskDtlResp.setRefCntrNo(gdnDtlDto.getRefCntrNo());

                    //物料描述
                    picktaskDtlResp.setMaterialDesc(gdnDtlDto.getMaterialDesc());

                }
            }
            //待检数量:【计划拣货数量】-【实际拣货数量】
            BigDecimal cmpQty = picktaskDtlResp.getCmpQty() == null ? BigDecimal.ZERO : picktaskDtlResp.getCmpQty();
            BigDecimal qty = picktaskDtlResp.getPlanQty().subtract(cmpQty);
            picktaskDtlResp.setWaitCheckQty(qty);
        }


        //设置拣货柜号
//        List<Integer> gdnHeaderIds = gdnDtlDtoList.stream().distinct().filter(s -> s.getGdnHeaderId() != null).map(s -> s.getGdnHeaderId()).collect(Collectors.toList());


        //拣货任务号
        picktaskDtlRespList.stream().forEach(s -> s.setPicktaskNo(picktaskHeaderResp.getPicktaskNo()));

        //设置库区
        List<Integer> zoneIdList = picktaskDtlRespList.stream()
                .distinct()
                .filter(s -> s.getZoneId() != null)
                .map(s -> s.getZoneId())
                .collect(Collectors.toList());


        if (zoneIdList.size() > 0) {

            List<Zone> zoneList = zoneService.getZoneListByZoneIdList(zoneIdList);

            for (PicktaskDtlResp picktaskDtlResp : picktaskDtlRespList) {
                for (Zone zone : zoneList) {
                    if (zone.getZoneId().equals(picktaskDtlResp.getZoneId())) {
                        picktaskDtlResp.setZoneDesc(zone.getZoneName());
                    }
                }
            }
        }


        //设置收货单号
        List<Long> headIdList = gdnDtlDtoList.stream()
                .distinct()
                .filter(s -> s.getGdnHeaderId() != null)
                .map(s -> s.getGdnHeaderId()).collect(Collectors.toList());

        List<GdnHeader> gdnHeaderList = gdnCoreService.getHeadByHeadIdList(headIdList);

        for (GdnDtlDto gdnDtlDto : gdnDtlDtoList) {
            for (GdnHeader gdnHeader : gdnHeaderList) {
                if (gdnHeader.getGdnHeaderId().equals(gdnDtlDto.getGdnHeaderId())) {
                    gdnDtlDto.setGdnNo(gdnHeader.getGdnNo());
                    gdnDtlDto.setRefCntrNo(gdnHeader.getRefCntrNo());
                }
            }
        }

        for (GdnDtlDto gdnDtlDto : gdnDtlDtoList) {
            for (PicktaskDtlResp picktaskDtlResp : picktaskDtlRespList) {
                if (gdnDtlDto.getGdnDtlId().equals(picktaskDtlResp.getDocDtlId())) {
                    //设置发货单号
                    picktaskDtlResp.setGdnNo(gdnDtlDto.getGdnNo());
                    //设置参考柜号
                    picktaskDtlResp.setRefCntrNo(gdnDtlDto.getRefCntrNo());
                }
            }
        }

        picktaskDtlRespList = picktaskDtlRespList.stream().sorted(Comparator.comparing(PicktaskDtlResp::getPicktaskLineNo)).collect(Collectors.toList());

        picktaskDtlInfoResp.setPicktaskDtlRespList(picktaskDtlRespList);
        picktaskDtlInfoResp.setPicktaskHeaderResp(picktaskHeaderResp);


        result.setData(picktaskDtlInfoResp);

        return result;
    }

    /**
     * 获取拣货子任务信息
     *
     * @param picktaskDtlId
     * @param picktaskLineNo
     * @param servletRequest
     * @param servletResponse
     */
    public PicktaskResp getTaskInfo(Integer picktaskDtlId,
                                    Integer picktaskLineNo,
                                    HttpServletRequest servletRequest,
                                    HttpServletResponse servletResponse) {


        PicktaskResp picktaskResp = new PicktaskResp();

        //拣货任务详情,拣货PDA子任务详情查询开始-------------START
        PicktaskDtl picktaskDtl = picktaskDtlService.getByPkAndLineNo(picktaskDtlId, picktaskLineNo);
        if (picktaskDtl == null) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "未查询到拣货明细");
        }

        PicktaskDtlResp picktaskDtlResp = new PicktaskDtlResp();

        BeanUtils.copyProperties(picktaskDtl, picktaskDtlResp);

        //设置待检数量
        BigDecimal cmpQty = picktaskDtlResp.getCmpQty() == null ? BigDecimal.ZERO : picktaskDtlResp.getCmpQty();
        BigDecimal subtract = picktaskDtlResp.getPlanQty().subtract(cmpQty);
        picktaskDtlResp.setWaitCheckQty(subtract);

        //根据发货单明细ID查询【发货单明细】
        Long gdnDtlId = picktaskDtlResp.getDocDtlId();
        GdnDtl gdnDtl = gdnCoreService.getGdnDtlByGdnDtlId(gdnDtlId);

        //客户型号
        picktaskDtlResp.setModel(gdnDtl.getCustomerModle());
        //发货单行号
        picktaskDtlResp.setLineNo(gdnDtl.getGdnLineNo());
        //订单号
        picktaskDtlResp.setPoNo(gdnDtl.getPoNo());

        //根据发货单明细的SKU_ID查询SKU信息:设置物料编码
        Integer skuId = gdnDtl.getSkuId();
        Sku sku = skuService.getSkuByPk(skuId);

        picktaskDtlResp.setMaterialCode(sku.getSkuCode());
        picktaskDtlResp.setMaterialDesc(sku.getDescCn());

        //根据SKU的货主OWNER查询客户信息
        Integer owner = sku.getOwner();
        CustomerDto customer = basicGetCustomerApi.getCustomer(owner);

        //客户
        picktaskDtlResp.setCustomerName(customer.getCustomerName());

        //根据发货单明细的查装柜单信息
        GdnHeader gdnHeader = gdnCoreService.getHeadByHeadId(gdnDtl.getGdnHeaderId());

//        CtnrLoading ctnrLoading = ctnrLoadingService.selectByGdnDtlId(gdnDtlId);
        //参考柜号
        picktaskDtlResp.setRefCntrNo(gdnHeader.getRefCntrNo());
        //设置发货单号
        picktaskDtlResp.setGdnNo(gdnHeader.getGdnNo());

        //根据拣货任务的单头ID查询拣货任务单号
        Integer picktaskHeaderId = picktaskDtlResp.getPicktaskHeaderId();

        PicktaskHeader picktaskHeader = picktaskHeaderService.getByPk(picktaskHeaderId);

        //设置拣货任务号
        picktaskDtlResp.setPicktaskNo(picktaskHeader.getPicktaskNo());

        //设置回参
        picktaskResp.setPicktaskDtlResp(picktaskDtlResp);

        //拣货任务详情,拣货PDA子任务详情查询完成------------------END

        //查询库位库存-------------------------START
        List<RepertoryResp> repertoryRespList = new ArrayList<>();

        /**
         * 逻辑：
         * 拣货任务明细(WMS_PICKTASK_DTL)，获取发货单明细ID，查询发货单明细表(WMS_GDN_DTL)，发货单预配表(WMS_GDN_PRE_ALOC)获取预配的库区，
         * 在这个库存区下，根据批次信息，获取当前发货单明细可用库位库存信息。（库存 = 数量-冻结数-分配数)
         */
//        DocPreAloc gdnPreAloc = gdnCoreService.getGdnPreAlocByGdnId(gdnDtlId, commonFlag);


        Integer docPreAlocId = picktaskDtl.getDocPreAlocId();
        DocPreAloc gdnPreAloc = docPreAlocService.selectByPk(docPreAlocId);

        //库区ID
        Integer areaId = gdnPreAloc.getZoneId();

        //批次号
        String lotno = gdnPreAloc.getLotno();

        String factory = gdnPreAloc.getFactory();

        Integer whsId = gdnPreAloc.getWhsId();

//        if (areaId != null) {
//            //根据库区ID查询库位集合
//            List<Location> locationList = locationService.selectLocationListByAreaId(areaId);
//
//            for (Location location : locationList) {
//                RepertoryResp repertoryResp = new RepertoryResp();
//                BeanUtils.copyProperties(location, repertoryResp);
//
//                repertoryRespList.add(repertoryResp);
//            }
//
//            //根据发货预配表的【批次号】和【库位ID集合】查询【库位库存】
//            //库位Id
//            List<Integer> locIdList = repertoryRespList.stream().map(s -> s.getLocId()).collect(Collectors.toList());
//            //库位库存
//            List<InvLoc> invLocList = invLocService.selectByLocIdListAnLotnoAndFatory(locIdList, gdnPreAloc);
//
//            for (RepertoryResp repertoryResp : repertoryRespList) {
//                for (InvLoc invLoc : invLocList) {
//                    if (repertoryResp.getLocId().equals(invLoc.getLocId())) {
//                        BigDecimal qty = invLoc.getQty();//数量
//                        BigDecimal holdQty = invLoc.getHoldQty();//冻结数量
//                        BigDecimal alocQty = invLoc.getAlocQty();//分配数量
//                        BigDecimal preOutQty = invLoc.getPreOutQty();//预支数量
//                        BigDecimal resultBig = qty.subtract(holdQty).subtract(alocQty).subtract(preOutQty);
//                        repertoryResp.setCount(resultBig.intValue());
//                    }
//                }
//            }
//        } else {
//            //查询那些【未上架】的库位库存
//            List<InvLoc> invLocList = invLocService.selectByLocIdListAnLotnoAndFatory(null, gdnPreAloc);
//
//            for (InvLoc invLoc : invLocList) {
//                RepertoryResp repertoryResp = new RepertoryResp();
//                repertoryResp.setLocCode("待上架");
//                repertoryResp.setLocDesc("待上架");
//
//                BigDecimal qty = invLoc.getQty();//数量
//                BigDecimal holdQty = invLoc.getHoldQty();//冻结数量
//                BigDecimal alocQty = invLoc.getAlocQty();//分配数量
//                BigDecimal preOutQty = invLoc.getPreOutQty();//预支数量
//                BigDecimal resultBig = qty.subtract(holdQty).subtract(alocQty).subtract(preOutQty);
//                repertoryResp.setCount(resultBig.intValue());
//                repertoryRespList.add(repertoryResp);
//            }
//        }

        //查询仓库---------------------------------------------

        //1.空库位
        //查询那些【未上架】的库位库存
//        List<InvLoc> invLocList = invLocService.selectByLocIdListAnLotnoAndFatory(null, gdnPreAloc);
//
//        for (InvLoc invLoc : invLocList) {
//            RepertoryResp repertoryResp = new RepertoryResp();
//            repertoryResp.setLocCode("待上架");
//            repertoryResp.setLocDesc("待上架");
//
//            BigDecimal qty = invLoc.getQty();//数量
//            BigDecimal holdQty = invLoc.getHoldQty();//冻结数量
//            BigDecimal alocQty = invLoc.getAlocQty();//分配数量
//            BigDecimal preOutQty = invLoc.getPreOutQty();//预支数量
//            BigDecimal resultBig = qty.subtract(holdQty).subtract(alocQty).subtract(preOutQty);
//            repertoryResp.setCount(resultBig.intValue());
//            repertoryRespList.add(repertoryResp);
//        }
//        //2.本库区
//        //根据库区ID查询库位集合
//        if (areaId != null) {
//            List<Location> locationList = locationService.selectLocationListByAreaId(areaId);
//
//            List<RepertoryResp> repertoryRespList2 = new ArrayList<>();
//            for (Location location : locationList) {
//                RepertoryResp repertoryResp = new RepertoryResp();
//                BeanUtils.copyProperties(location, repertoryResp);
//
//                repertoryRespList2.add(repertoryResp);
//            }
//
//            //根据发货预配表的【批次号】和【库位ID集合】查询【库位库存】
//            //库位Id
////            List<Integer> locIdList = repertoryRespList.stream().filter(s -> s.getLocId() != null).map(s -> s.getLocId()).collect(Collectors.toList());
//            List<Integer> locIdList = repertoryRespList2.stream().map(s -> s.getLocId()).collect(Collectors.toList());
//            //库位库存
//            List<InvLoc> invLocListOwn = invLocService.selectByLocIdListAnLotnoAndFatory(locIdList, gdnPreAloc);
//            for (RepertoryResp repertoryResp : repertoryRespList2) {
//                for (InvLoc invLoc : invLocListOwn) {
//                    if (repertoryResp.getLocId().equals(invLoc.getLocId())) {
//                        BigDecimal qty = invLoc.getQty();//数量
//                        BigDecimal holdQty = invLoc.getHoldQty();//冻结数量
//                        BigDecimal alocQty = invLoc.getAlocQty();//分配数量
//                        BigDecimal preOutQty = invLoc.getPreOutQty();//预支数量
//                        BigDecimal resultBig = qty.subtract(holdQty).subtract(alocQty).subtract(preOutQty);
//                        repertoryResp.setCount(resultBig.intValue());
//                    }
//                }
//            }
//
//            repertoryRespList.addAll(repertoryRespList2);
//        }
        //3.本仓库，其他库位
        //库位Id
        List<Integer> locIdListOther = new ArrayList<>();
        //库位库存
        List<InvLoc> invLocListOther = invLocService.selectByLocIdListAnLotnoAndFatory(locIdListOther, gdnPreAloc);

        List<Integer> allLocIdList = invLocListOther.stream().filter(s -> s.getLocId() != null).map(s -> s.getLocId()).collect(Collectors.toList());

        List<Location> allLocationList = new ArrayList<>();
        if (allLocIdList.size() > 0) {
            allLocationList = locationService.selectByLocIdList(allLocIdList);
        }

        Map<Integer, List<Location>> allMap = allLocationList.stream().collect(Collectors.groupingBy(s -> s.getLocId()));

        for (InvLoc invLoc : invLocListOther) {

            if (allMap.containsKey(invLoc.getLocId())) {
                List<Location> locations = allMap.get(invLoc.getLocId());
                Location location = locations.get(0);

                RepertoryResp repertoryResp = new RepertoryResp();
                repertoryResp.setLocCode(location.getLocCode());
                repertoryResp.setLocDesc(location.getLocDesc());
                repertoryResp.setLocId(location.getLocId());

                BigDecimal qty = invLoc.getQty();//数量
                BigDecimal holdQty = invLoc.getHoldQty();//冻结数量
                BigDecimal alocQty = invLoc.getAlocQty();//分配数量
                BigDecimal preOutQty = invLoc.getPreOutQty();//预支数量
                BigDecimal resultBig = qty.subtract(holdQty).subtract(alocQty).subtract(preOutQty);
                repertoryResp.setCount(resultBig.intValue());
                repertoryRespList.add(repertoryResp);

            } else {
                RepertoryResp repertoryResp = new RepertoryResp();
                repertoryResp.setLocCode("待上架");
                repertoryResp.setLocDesc("待上架");

                BigDecimal qty = invLoc.getQty();//数量
                BigDecimal holdQty = invLoc.getHoldQty();//冻结数量
                BigDecimal alocQty = invLoc.getAlocQty();//分配数量
                BigDecimal preOutQty = invLoc.getPreOutQty();//预支数量
                BigDecimal resultBig = qty.subtract(holdQty).subtract(alocQty).subtract(preOutQty);
                repertoryResp.setCount(resultBig.intValue());
                repertoryRespList.add(repertoryResp);
            }
        }



        repertoryRespList = repertoryRespList.stream()
                .filter(s -> s.getCount() != null)
                .filter(s -> s.getCount() > 0)
                .collect(Collectors.toList());

        List<RepertoryResp> newList = new ArrayList<>();
        //排序
//        List<RepertoryResp> nullLocList = repertoryRespList.stream().filter(s -> s.getLocId() == null).collect(Collectors.toList());
        List<RepertoryResp> nullLocList = repertoryRespList.stream().filter(s -> s.getLocCode().equals("待上架")).collect(Collectors.toList());
        repertoryRespList.removeAll(nullLocList);
        //过滤掉空库位

        newList.addAll(nullLocList);

        if (areaId != null) {
            List<Location> locationList = locationService.selectLocationListByAreaId(areaId);
            List<Integer> locIdList = locationList.stream().map(s -> s.getLocId()).collect(Collectors.toList());

            //本库区的
            List<RepertoryResp> ownZoneList = repertoryRespList.stream().filter(s -> s.getLocId() != null).filter(s -> locIdList.contains(s.getLocId())).collect(Collectors.toList());
            newList.addAll(ownZoneList);

            repertoryRespList.removeAll(ownZoneList);
        }

        newList.addAll(repertoryRespList);
        repertoryRespList.clear();
        repertoryRespList.addAll(newList);




//        List<Integer> locIdSelectList = repertoryRespList.stream().map(s -> s.getLocId()).collect(Collectors.toList());
//
//        //过滤前面俩已经查过的
//        invLocListOther = invLocListOther.stream().filter(s->!locIdSelectList.contains(s.getLocId())).collect(Collectors.toList());
//
//        List<Integer> locIdListOth = invLocListOther.stream().map(s -> s.getLocId()).distinct().collect(Collectors.toList());
//
//        List<Location> locationListOth = locationService.selectByLocIdList(locIdListOth);
//        Map<Integer, List<Location>> locIdMap = locationListOth.stream().collect(Collectors.groupingBy(s -> s.getLocId()));
//
//        for (InvLoc invLoc : invLocListOther) {
//            if (locIdMap.containsKey(invLoc.getLocId())) {
//                List<Location> locations = locIdMap.get(invLoc.getLocId());
//                Location location = locations.get(0);
//
//                RepertoryResp repertoryResp = new RepertoryResp();
//                repertoryResp.setLocCode(location.getLocCode());
//                repertoryResp.setLocDesc(location.getLocDesc());
//
//                BigDecimal qty = invLoc.getQty();//数量
//                BigDecimal holdQty = invLoc.getHoldQty();//冻结数量
//                BigDecimal alocQty = invLoc.getAlocQty();//分配数量
//                BigDecimal preOutQty = invLoc.getPreOutQty();//预支数量
//                BigDecimal resultBig = qty.subtract(holdQty).subtract(alocQty).subtract(preOutQty);
//                repertoryResp.setCount(resultBig.intValue());
//                repertoryRespList.add(repertoryResp);
//            }
//        }
        //3.本仓库，其他库位


        //查询仓库---------------------------------------------



        repertoryRespList = repertoryRespList.stream()
                .filter(s -> s.getCount() != null)
                .filter(s -> s.getCount() > 0)
                .collect(Collectors.toList());


        //设置回参
        picktaskResp.setRepertoryRespList(repertoryRespList);

        //查询库位库存---------------------------END

        //查询拣货任务货品清单，根据拣货任务明细ID-----------start
        List<PicktaskDtlGoods> picktaskDtlGoodsList = picktaskDtlGoodsService.selectByPickDtlId(picktaskDtlId);

        List<SnDtlResp> snDtlRespList = new ArrayList<>();

        for (PicktaskDtlGoods picktaskDtlGoods : picktaskDtlGoodsList) {
            SnDtlResp snDtlResp = new SnDtlResp();

            snDtlResp.setSn(picktaskDtlGoods.getGoodsSn());//SN
            snDtlResp.setPicktaskTime(picktaskDtlGoods.getCreateTime());//拣货时间
            snDtlResp.setCount(picktaskDtlGoods.getQty());//数量

            snDtlRespList.add(snDtlResp);
        }

        picktaskResp.setSnDtlRespList(snDtlRespList);
        //查询拣货任务货品清单，根据拣货任务明细ID-------------end


        //如果单头【状态】等于【已拣货】
        if (picktaskHeader.getStatus().equals(PicktaskStatusEnum.PICK_ED.getCode())) {
            picktaskResp.setFinishedFlag(1);
        } else {
            picktaskResp.setFinishedFlag(0);
        }

        return picktaskResp;
    }


    /**
     * @param picktaskDtlId
     * @param sn
     * @param scanMode        0:单台，1:整板
     * @param servletRequest
     * @param servletResponse
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Integer scanSn(Integer picktaskDtlId, String sn, Integer scanMode, HttpServletRequest servletRequest,
                          HttpServletResponse servletResponse) throws Exception {

        Integer picktaskCount = 0;

        String loginName = HttpSessionUtil.getLoginName(servletRequest);
//        String loginName = "gxm";

        //检查-------------------------------------------start
        //TODO 查询系统配置区分整板/单台
        if (scanMode.equals(CommonProperty.COMMON_FALSE)) {

            Goods goods = goodsService.selectBySn(sn);
            String beforeGoodsJson = JSON.toJSONString(goods);

            //检查
            checkSnAllowPicking(picktaskDtlId, sn, CommonProperty.COMMON_FALSE, scanMode);

            //录入，更新状态
            picktaskCount = getScanCount(picktaskDtlId, sn, servletRequest);
            //TODO 测试代码，测试和投产记得删除
//            int a = 0 / 0;
            //清空PKG_ID
            log.info("单台清空PKG_ID");

            goods.setGoodsId(goods.getGoodsId());
            goods.setModifyUser(loginName);
            goods.setModifyTime(new Date());
            Integer row = goodsService.updateGoodsDePackage(goods);
            log.info("成功更新{}条记录", row);

            Goods goodsUpdate = goodsService.selectBySn(sn);
            String updateGoodsJson = JSON.toJSONString(goodsUpdate);
            GoodsLog goodsLog = new GoodsLog();
            goodsLog.setGoodsSn(goods.getGoodsSn());
            goodsLog.setOpCode(GoodsLogEnum.PICK.getCode());
            goodsLog.setReason(GoodsLogEnum.PICK.getEnumName());
            goodsLog.setOperator(loginName);
            goodsLog.setCreateTime(new Date());
            goodsLog.setCreateUser(loginName);

            String content = "未更新的:" + beforeGoodsJson + ",更新的:" + updateGoodsJson;
            goodsLog.setUpdateContent(content);
            boolean insFlag = goodsService.insertGoodsLog(goodsLog);
            log.info("成功新增1条记录,{}", insFlag);

        } else {

            List<SnInforDto> snInforDtoList = goodsService.selectSnInfosBySn(sn, SnScanTypeEnum.SELECT_PALLET);

            if (snInforDtoList.size() == 0) {
                Shift.fatal(StatusCode.BUSINESS_ERR, "未查询到该条码信息");
            }

            List<String> snList = snInforDtoList.stream().map(s -> s.getGoodsSn()).collect(Collectors.toList());
            List<Goods> goodsListBefore = goodsService.selectListByListSn(snList);



            //2）检查产品SN状态是否可拣货(状态=待上架(2)、已上架(3)、带下架(4))

            int waitUpCode = GoodsStatusEnum.WAIT_UP.getCode();
            int hasUpCode = GoodsStatusEnum.HAS_UP.getCode();
            int waitDowmCode = GoodsStatusEnum.WAIT_DOWM.getCode();

            snInforDtoList = snInforDtoList.stream()
                    .filter(s -> {
                        if (s.getStatus().equals(waitUpCode) || s.getStatus().equals(hasUpCode) || s.getStatus().equals(waitDowmCode)) {
                            return true;
                        } else {
                            return false;
                        }
                    })
                    .collect(Collectors.toList());

            if (snInforDtoList.size() == 0) {
                Shift.fatal(StatusCode.BUSINESS_ERR, "该栈板内无允许拣货的产品");
            }

            //检查栈板里的数量是否大于拣货的计划拣货数
            checkCountAllow(snInforDtoList, picktaskDtlId);


            List<String> goodsSnList = snInforDtoList.stream()
                    .filter(s -> s.getGoodsSn() != null)
                    .filter(s -> s.getStatus() != GoodsStatusEnum.HAS_DOWN.getCode())
                    .map(s -> s.getGoodsSn()).collect(Collectors.toList());


            //检查
            for (String goodsSn : goodsSnList) {
                checkSnAllowPicking(picktaskDtlId, goodsSn, CommonProperty.COMMON_TRUE, scanMode);
            }

            //录入，更新状态
            for (String snStr : goodsSnList) {
                picktaskCount += getScanCount(picktaskDtlId, snStr, servletRequest);
            }


            //更新后的
            List<Goods> goodsListUpdate = goodsService.selectListByListSn(snList);
            Map<String, List<Goods>> goodsMap = goodsListUpdate.stream().collect(Collectors.groupingBy(s -> s.getGoodsSn()));

            //写goods日志
            for (Goods goods : goodsListBefore) {
                if (goodsMap.containsKey(goods.getGoodsSn())) {
                    String beforeGoodsJson = JSON.toJSONString(goods);

                    String updateGoodsJson = JSON.toJSONString(goodsMap.get(goods.getGoodsSn()));
                    GoodsLog goodsLog = new GoodsLog();
                    goodsLog.setGoodsSn(goods.getGoodsSn());
                    goodsLog.setOpCode(GoodsLogEnum.PICK.getCode());
                    goodsLog.setReason(GoodsLogEnum.PICK.getEnumName());
                    goodsLog.setOperator(loginName);
                    goodsLog.setCreateTime(new Date());
                    goodsLog.setCreateUser(loginName);

                    String content = "未更新的:" + beforeGoodsJson + ",更新的:" + updateGoodsJson;
                    goodsLog.setUpdateContent(content);
                    boolean insFlag = goodsService.insertGoodsLog(goodsLog);
                    log.info("成功新增1条记录,{}", insFlag);
                }
            }
        }
//        int a = 0 / 0;
        return picktaskCount;
        //检查---------------------------------------------end
    }

    /**
     * 检查栈板内数量总和是否大于库存数（库存 = 数量-冻结数-分配数）
     *
     * @param snInforDtoList
     * @param picktaskDtlId
     */
    private void checkCountAllow(List<SnInforDto> snInforDtoList, Integer picktaskDtlId) {

//        Goods goods = goodsService.selectBySn(sn);
//
//        BigDecimal qty = invLoc.getQty();//数量
//        BigDecimal holdQty = invLoc.getHoldQty();//冻结数量
//        BigDecimal alocQty = invLoc.getAlocQty();//分配数量

//        BigDecimal resultBig = qty.subtract(holdQty).subtract(alocQty);

        BigDecimal listCount = snInforDtoList.stream().filter(s -> s.getQty() != null).map(s -> s.getQty()).reduce(BigDecimal.ZERO, BigDecimal::add);

        PicktaskDtl picktaskDtl = picktaskDtlService.selectByPicktaskDtlId(picktaskDtlId);
        BigDecimal planQty = picktaskDtl.getPlanQty();
        BigDecimal cmpQty = picktaskDtl.getCmpQty();
        if (cmpQty == null) {
            cmpQty = BigDecimal.ZERO;
        }

        BigDecimal waitQty = planQty.subtract(cmpQty);


        if (waitQty.compareTo(listCount) == -1) {
            String message = String.format("栈板内的数量:%s大于拣货任务的待拣数:%s", listCount, waitQty);
            Shift.fatal(StatusCode.BUSINESS_ERR, message);
        }
    }


    /**
     * 拣货的数量
     *
     * @param picktaskDtlId
     * @param sn
     * @param servletRequest
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public int getScanCount(Integer picktaskDtlId, String sn, HttpServletRequest servletRequest) throws Exception {

        String loginName = HttpSessionUtil.getLoginName(servletRequest);
//        String loginName = "gxm";

        //拣货明细
        PicktaskDtl picktaskDtl = picktaskDtlService.selectByPicktaskDtlId(picktaskDtlId);

        //更新发货单明细
        Long docDtlId = picktaskDtl.getDocDtlId();

        GdnDtl gdnDtlCountUpdate = gdnCoreService.getGdnDtlByGdnDtlId(docDtlId);

        //只录入状态是1（有效）的
//        DocPreAloc docPreAloc = gdnCoreService.getGdnPreAlocByGdnId(docDtlId, commonFlag);
        DocPreAloc docPreAloc = docPreAlocService.selectByPk(picktaskDtl.getDocPreAlocId());

        //DocPreAloc docPreAloc = docPreAlocService.selectByDocDtlId(docDtlId);

        log.info("执行SN:{}录入【更新】", sn);
        //变更产品SN状态 = 已下架(5)----------------------------start
        Goods goods = goodsService.selectBySn(sn);

        Goods goodsUpdate = new Goods();

        goodsUpdate.setModifyUser(loginName);
        Date nowDate = new Date();
        goodsUpdate.setModifyTime(nowDate);
        goodsUpdate.setGoodsId(goods.getGoodsId());
        goodsUpdate.setStatus(GoodsStatusEnum.HAS_DOWN.getCode());//已下架

        Integer row = goodsService.updateStatusByGoods(goodsUpdate);
        log.info("成功更新{}条记录", row);
        //变更产品SN状态 = 已下架(5)------------------------------end

        //变更产品SN对应的库位库存表【分配数】增加，库区库存表【预配数】减少、【分配数】增加------start

        //库位ID
        Integer locId = goods.getLocId();
        //库位信息
//        Location location = locationService.getLocationByLocId(locId);

        //根据库位ID+批次号查询库位库存
        String lotno = goods.getLotno();//批次号

        UpdateInventoryInputDto updateLocInventoryInputDto = new UpdateInventoryInputDto();

        updateLocInventoryInputDto.setWhsId(goods.getWhsId());
        updateLocInventoryInputDto.setLocId(goods.getLocId());
        updateLocInventoryInputDto.setOwner(goods.getOwner());
        updateLocInventoryInputDto.setSkuId(goods.getSkuId());
        updateLocInventoryInputDto.setFactory(goods.getFactory());
        updateLocInventoryInputDto.setVendorId(goods.getVendorId());
        updateLocInventoryInputDto.setLotNo(goods.getLotno());
        updateLocInventoryInputDto.setInvAttr(goods.getInvAttr());
        updateLocInventoryInputDto.setQaStatus(goods.getQaStatus());
        updateLocInventoryInputDto.setUnit(goods.getUnit());

        updateLocInventoryInputDto.setAlocQty(goods.getQty());
        updateLocInventoryInputDto.setOperator(loginName);

//        String key = "Key_Inv_Loc_"
//                + input.getWhsId() + "#loc" + input.getLocId() + "#" + input.getOwner() + "#"
//                + input.getSkuId() + "#" + input.getFactory() + "#" + input.getVendorId() + "#"
//                + input.getLotNo() + "#" + input.getInvAttr() + "#" + input.getQaStatus() + "#";
        /*Integer rowInv = */inventoryService.updateLocInventroy(updateLocInventoryInputDto);
        // log.info("成功更新{}条记录", rowInv);
        //todo ------------------end

        //库区库存表【预配数】减少、【分配数】增加

        String udf1 = docPreAloc.getUdf1();//库区库存ID

        InvZone invZoneSelect = new InvZone();

        UpdateInventoryInputDto zoneUpdate = new UpdateInventoryInputDto();

        zoneUpdate.setWhsId(goods.getWhsId());
        zoneUpdate.setLocId(goods.getLocId());
        zoneUpdate.setOwner(goods.getOwner());
        zoneUpdate.setSkuId(goods.getSkuId());
        zoneUpdate.setFactory(goods.getFactory());
        zoneUpdate.setVendorId(goods.getVendorId());
        zoneUpdate.setLotNo(goods.getLotno());
        zoneUpdate.setInvAttr(goods.getInvAttr());
        zoneUpdate.setQaStatus(goods.getQaStatus());
        zoneUpdate.setUnit(goods.getUnit());

        zoneUpdate.setAlocQty(goods.getQty());//分配数量
        //【拣货-PDA】功能调整,不使用预配数，_2021_5_31
//        zoneUpdate.setPreAlocQty(goods.getQty().negate());//预分配数量
        zoneUpdate.setOperator(loginName);


        /*Integer updateZoneInventory = */inventoryService.updateZoneInventory(zoneUpdate);
        //log.info("成功更新{}条记录", updateZoneInventory);
        //变更产品SN对应的库位库存表【分配数】增加，库区库存表【预配数】减少、【分配数】增加--------end


        //3）拣货任务明细货品清单(WMS_PICKTASK_DTL_GOODS)插入扫描的SN信息----------START

        PicktaskDtlGoods picktaskDtlGoods = new PicktaskDtlGoods();
        picktaskDtlGoods.setCreateUser(loginName);
        picktaskDtlGoods.setCreateTime(nowDate);

        picktaskDtlGoods
                .setGoodsSn(goods.getGoodsSn())
                .setGoodsId(goods.getGoodsId())
                .setQty(goods.getQty())
                .setPicktaskDtlId(picktaskDtlId)
                .setStatus(1);//TODO 后面看文档改

        Integer addOne = picktaskDtlGoodsService.addOne(picktaskDtlGoods);
        log.info("成功新增{}条记录", addOne);
        //3）拣货任务明细货品清单(WMS_PICKTASK_DTL_GOODS)插入扫描的SN信息------------END

        //4）数量&状态更新------------------------------------------------------------------START
        /**
         * ①拣货任务明细表(WMS_PICKTASK_DTL)实际拣货数(CMP_QTY)增加；对应的发货单明细(WMS_GDN_DTL)拣货数量(PICK_QTY)增加。
         * ②拣货任务明细表(WMS_PICKTASK_DTL)当计划拣货数量(PLAN_QTY)=实际拣货数(CMP_QTY)时，状态(STATUS)=已拣货(3)；
         * ③对应的发货单明细(WMS_GDN_DTL)，订单数量(ORDER_QTY) =拣货数量(PICK_QTY)时，状态(STATUS)= 待装柜(5)，
         * 订单数量(ORDER_QTY) <拣货数量(PICK_QTY)时，状态(STATUS)= 拣货中(4)
         * ④拣货任务明细表(WMS_PICKTASK_DTL)全部拣货任务明细表状态状态(STATUS) =已拣货(3)，拣货任务表头的状态(STATUS) =已拣货(3)，
         * 否则拣货任务表头的状态=进行中(3)；
         */

        //①
        //更新拣货任务明细


        PicktaskDtl picktaskDtlUpdate = new PicktaskDtl();
//        BigDecimal cmpQty = picktaskDtl.getCmpQty();
//        if (cmpQty == null) {
//            cmpQty = BigDecimal.ZERO;
//        }

        BigDecimal qty = goods.getQty();
        if (qty == null) {
            qty = BigDecimal.ZERO;
        }

        picktaskDtlUpdate.setPicktaskDtlId(picktaskDtlId);

        picktaskDtlUpdate.setModifyTime(nowDate);
        picktaskDtlUpdate.setModifyUser(loginName);
        picktaskDtlUpdate.setStatus(PicktaskStatusEnum.PICK_ING.getCode());

        //TODO
//        Integer rowPickdtl = picktaskDtlService.updateCmpQtyByPk(picktaskDtlUpdate);
        Integer rowPickdtl = picktaskDtlService.updateCmpQtyByDtlId(picktaskDtlUpdate, qty);
        if (rowPickdtl == 0) {
            Shift.fatal(StatusCode.OPERATE_ERR,"录入的实际拣货数量不能超出本明细计划拣货数量");
        }
        log.info("成功更新【拣货任务明细表(WMS_PICKTASK_DTL)】{}条记录", rowPickdtl);


//        gdnDtlCountUpdate.setGdnDtlId(docDtlId);
        BigDecimal pickQty = gdnDtlCountUpdate.getPickQty();
        if (pickQty == null) {
            pickQty = BigDecimal.ZERO;
        }

//        gdnDtlCountUpdate.setPickQty(pickQty.add(goods.getQty()));

        //更新数量
//        Integer rowUpdateGdnDtl = gdnCoreService.updateGdnDtlPickQty(docDtlId, gdnDtlCountUpdate.getPickQty(), loginName);
//        Integer rowUpdateGdnDtl = gdnCoreService.updateGdnDtlPickQty(docDtlId, qty, loginName);
        Integer rowUpdateGdnDtl = gdnCoreService.updateGdnDtlPickQty2(docDtlId, qty, loginName);
        log.info("成功更新{}条记录", rowUpdateGdnDtl);
        //TODO 测试代码，测试和投产记得删除
//        int a = 0 / 0;


        //②
        PicktaskDtl picktaskDtlNew = picktaskDtlService.selectByPicktaskDtlId(picktaskDtlId);
        if (picktaskDtlNew.getPlanQty().equals(picktaskDtlNew.getCmpQty())) {
            Integer rowUpdateStatus = picktaskDtlService.updateStatusByPk(picktaskDtlId, PicktaskStatusEnum.PICK_ED.getCode(), loginName, nowDate);
            log.info("成功更新{}条记录", rowUpdateStatus);
        }

        //③
        //发货单明细ID
        Long gdnDtlId = docDtlId;
        GdnDtl gdnDtl = gdnCoreService.getGdnDtlByGdnDtlId(gdnDtlId);
//        if (gdnDtl.getOrderQty().subtract(gdnDtl.getPickQty()) == BigDecimal.ZERO) {
//            //订单数量(ORDER_QTY) =拣货数量(PICK_QTY)时，状态(STATUS)= 待装柜(5)，
//            Integer rowGdn = gdnCoreService.updateGdnDtlStatusByPk(gdnDtlId, GdnDtlStatusEnum.WAITLOAD.getCode(), loginName, nowDate);
//            log.info("成功更新{}条记录", rowGdn);
//
////            picktaskDtlService.updateStatusByPk(picktaskDtlId, PicktaskStatusEnum.PICK_ED.getCode(), loginName,nowDate )
//
//        } else if (gdnDtl.getOrderQty().compareTo(gdnDtl.getPickQty()) == 1) {
//            //订单数量(ORDER_QTY) <拣货数量(PICK_QTY)时，状态(STATUS)= 拣货中(4)
//            Integer rowGdn = gdnCoreService.updateGdnDtlStatusByPk(gdnDtlId, GdnDtlStatusEnum.PICKING.getCode(), loginName, nowDate);
//            log.info("成功更新【发货单明细】{}条记录", rowGdn);
//            //明细状态为4，则header状态为3进行中
//            Integer headerRow = gdnCoreService.updateGdnHeaderStatusByPk(gdnDtl.getGdnHeaderId(), GdnHeaderStatusEnum.DOING.getCode(), loginName, nowDate);
//            log.info("成功更新【发货单单头】{}条记录", headerRow);
//        }
        //原有的👆




        //TODO 更新的 ZYF:主档状态为7/8，不更新明细
        GdnHeader gdnHeader = gdnHeaderMapper.selectOne(new QueryWrapper<GdnHeader>().eq("GDN_HEADER_ID", gdnDtl.getGdnHeaderId()));
        if (!(gdnHeader.getStatus().equals(GdnHeaderStatusEnum.FINISH.code)||gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CLOSE.code))){
            if (gdnDtl.getShipQty().compareTo(BigDecimal.ZERO) == 0) {
                if (gdnDtl.getOrderQty().subtract((gdnDtl.getPickQty()==null?BigDecimal.ZERO:gdnDtl.getPickQty())) == BigDecimal.ZERO) {
                    //订单数量(ORDER_QTY) =拣货数量(PICK_QTY)时，状态(STATUS)= 待装柜(5)，
                    Integer rowGdn = gdnCoreService.updateGdnDtlStatusByPk(gdnDtlId, GdnDtlStatusEnum.WAITLOAD.getCode(), loginName, nowDate);
                    log.info("成功更新{}条记录", rowGdn);
                } else if ((gdnDtl.getPickQty()==null?BigDecimal.ZERO:gdnDtl.getPickQty()).compareTo(BigDecimal.ZERO) == 0) {
                    Integer rowGdn = gdnCoreService.updateGdnDtlStatusByPk(gdnDtlId, GdnDtlStatusEnum.WAITPICK.getCode(), loginName, nowDate);
                    log.info("成功更新{}条记录", rowGdn);
                } else if (gdnDtl.getOrderQty().compareTo((gdnDtl.getPickQty()==null?BigDecimal.ZERO:gdnDtl.getPickQty())) == 1) {
                    Integer rowGdn = gdnCoreService.updateGdnDtlStatusByPk(gdnDtlId, GdnDtlStatusEnum.PICKING.getCode(), loginName, nowDate);
                    log.info("成功更新{}条记录", rowGdn);
                }

            } else if (gdnDtl.getShipQty().compareTo(BigDecimal.ZERO) > 0) {
                if (gdnDtl.getOrderQty().compareTo(gdnDtl.getShipQty()) == 1) {
                    Integer rowGdn = gdnCoreService.updateGdnDtlStatusByPk(gdnDtlId, GdnDtlStatusEnum.LOADING.getCode(), loginName, nowDate);
                    log.info("成功更新{}条记录", rowGdn);
                }
            }
            GdnHeader gdnHeaderUpdate = new GdnHeader();
            gdnHeaderUpdate.setStatus(GdnHeaderStatusEnum.DOING.getCode());
            gdnHeaderUpdate.setModifyTime(nowDate);
            gdnHeaderUpdate.setModifyUser(loginName);

            //创建类型的条件构造器
            QueryWrapper<GdnHeader> wrapper = new QueryWrapper<>();
            wrapper.ne("STATUS", GdnHeaderStatusEnum.DOING.getCode());
            wrapper.eq("GDN_HEADER_ID", gdnHeader.getGdnHeaderId());

            int updateRow = gdnHeaderMapper.update(gdnHeaderUpdate, wrapper);
            log.info("成功更新【发货单单头】{}条记录", updateRow);

        }


//        if (gdnDtl.getOrderQty().subtract(gdnDtl.getPickQty()) == BigDecimal.ZERO) {
//            //订单数量(ORDER_QTY) =拣货数量(PICK_QTY)时，状态(STATUS)= 待装柜(5)，
//            Integer rowGdn = gdnCoreService.updateGdnDtlStatusByPk(gdnDtlId, GdnDtlStatusEnum.WAITLOAD.getCode(), loginName, nowDate);
//            log.info("成功更新{}条记录", rowGdn);
//
////            picktaskDtlService.updateStatusByPk(picktaskDtlId, PicktaskStatusEnum.PICK_ED.getCode(), loginName,nowDate )
//
//        } else if (gdnDtl.getOrderQty().compareTo(gdnDtl.getPickQty()) == 1) {
//            //订单数量(ORDER_QTY) <拣货数量(PICK_QTY)时，状态(STATUS)= 拣货中(4)
//            Integer rowGdn = gdnCoreService.updateGdnDtlStatusByPk(gdnDtlId, GdnDtlStatusEnum.PICKING.getCode(), loginName, nowDate);
//            log.info("成功更新【发货单明细】{}条记录", rowGdn);
//            //明细状态为4，则header状态为3进行中
//            Integer headerRow = gdnCoreService.updateGdnHeaderStatusByPk(gdnDtl.getGdnHeaderId(), GdnHeaderStatusEnum.DOING.getCode(), loginName, nowDate);
//            log.info("成功更新【发货单单头】{}条记录", headerRow);
//        }
        //TODO 更新的

        //④
        Integer picktaskHeaderId = picktaskDtl.getPicktaskHeaderId();
        PicktaskHeader picktaskHeader = picktaskHeaderService.getByPk(picktaskHeaderId);

        List<PicktaskDtl> picktaskDtlList = picktaskDtlService.getDtlByHeadId(picktaskHeaderId);


        Integer count = Math.toIntExact(picktaskDtlList.stream().map(s -> s.getStatus()).filter(s -> s.equals(PicktaskStatusEnum.PICK_ED.getCode())).count());

        if (count.equals(picktaskDtlList.size())) {
            Integer rowHead = picktaskHeaderService.updateStatusByPK(picktaskHeaderId, PicktaskStatusEnum.PICK_ED.getCode(), loginName, nowDate);
            log.info("成功更新{}条记录", rowHead);
        } else {
            Integer rowHead = picktaskHeaderService.updateStatusByPK(picktaskHeaderId, PicktaskStatusEnum.PICK_ING.getCode(), loginName, nowDate);
            log.info("成功更新{}条记录", rowHead);
        }

        //4）数量&状态更新--------------------------------------------------------------------end


        log.info("执行SN:{} 录入【更新】结束", sn);
//        int a = 0 / 0;
        return goods.getQty().intValue();
    }

    /**
     * 检查SN是否允许拣货
     *
     * @param picktaskDtlId
     * @param sn
     * @param scanMode
     * @param modeType      栈板：1，单台：0
     */
    private void checkSnAllowPicking(Integer picktaskDtlId, String sn, Integer scanMode, Integer modeType) {

        String prefix = new String();
        if (Objects.equals(modeType, commonFlag)) {
            prefix = "该栈板下的";
        }

        log.info("执行SN检查【开始】,SN:{}", sn);

        PicktaskDtl picktaskDtl = picktaskDtlService.selectByPicktaskDtlId(picktaskDtlId);

        //1）检查产品SN的物料是否准确
        Goods goods = goodsService.selectBySn(sn);
        if (goods == null) {

            String message = String.format("条码:【%s】未在货品清单中存在", sn);
            if (Objects.equals(modeType, commonFlag)) {
                message = prefix + message;
            }
            Shift.fatal(StatusCode.BUSINESS_ERR, message);
        }

        if (!Objects.equals(goods.getQaStatus(), commonFlag)) {

            String message = String.format("条码:【%s】的品质状态不是检验合格", sn);
            if (Objects.equals(modeType, commonFlag)) {
                message = prefix + message;
            }
            Shift.fatal(StatusCode.BUSINESS_ERR, message);
        }

        //校验是否同一组织
        Integer docPreAlocId = picktaskDtl.getDocPreAlocId();
        DocPreAloc docPreAloc = docPreAlocService.selectByPk(docPreAlocId);
        String factory = docPreAloc.getFactory();

        if (!Objects.equals(factory, goods.getFactory())) {
            String message = String.format("条码:【%s】的组织:【%s】与该单据组织:【%s】不一致，拒绝跨组织拣货", sn, goods.getFactory(), factory);
            if (Objects.equals(modeType, commonFlag)) {
                message = prefix + message;
            }
            Shift.fatal(StatusCode.BUSINESS_ERR, message);
        }


        Integer headerId = picktaskDtl.getPicktaskHeaderId();

//        PicktaskHeader pkHeader = picktaskHeaderService.getByPk(headerId);

        PicktaskDtlGoods picktaskDtlGoods = picktaskDtlGoodsService.selectByGoodsSnAndDtlIdHeadId(sn, picktaskDtlId, headerId);
        if (picktaskDtlGoods != null) {

            String message = String.format("条码:【%s】的货品在已该拣货单中拣货，拒绝重复拣货", sn);
            if (Objects.equals(modeType, commonFlag)) {
                message = prefix + message;
            }

            Shift.fatal(StatusCode.BUSINESS_ERR, message);
        }

        Integer locId = goods.getLocId();
        String lotno = goods.getLotno();
        //新加的:检查栈板内数量总和是否大于库存数（库存 = 数量-冻结数-分配数）===start

        if (!scanMode.equals(commonFlag)) {

//
//            BigDecimal qty = invLoc.getQty();//数量
//            BigDecimal holdQty = invLoc.getHoldQty();//冻结数量
//            BigDecimal alocQty = invLoc.getAlocQty();//分配数量

//            BigDecimal resultBig = qty.subtract(holdQty).subtract(alocQty);
            BigDecimal planQty = picktaskDtl.getPlanQty();
            BigDecimal cmpQty = picktaskDtl.getCmpQty();
            if (cmpQty == null) {
                cmpQty = BigDecimal.ZERO;
            }
            BigDecimal waitQty = planQty.subtract(cmpQty);

            if (waitQty.compareTo(goods.getQty()) == -1) {

                String message = message = String.format("条码:【%s】的数量:%s大于拣货任务的待拣数:%s", sn, goods.getQty(), waitQty);
                if (Objects.equals(modeType, commonFlag)) {
                    message = prefix + message;
                }

                Shift.fatal(StatusCode.BUSINESS_ERR, message);
            }
        }

        //新加的:检查栈板内数量总和是否大于库存数（库存 = 数量-冻结数-分配数） ===end


        //2）检查产品SN状态是否可拣货(状态=待上架(2)、已上架(3)、已下架(4))
        Integer goodsStatus = goods.getStatus();

        int waitUpCode = GoodsStatusEnum.WAIT_UP.getCode();//待上架
        int hasUpCode = GoodsStatusEnum.HAS_UP.getCode();//已上架
        int waitDowmCode = GoodsStatusEnum.WAIT_DOWM.getCode();//待下架

        GoodsStatusEnum goodsStatusEnum = GoodsStatusEnum.getEnumByCode(goodsStatus);
        String enumName = goodsStatusEnum.getEnumName();

        List<Integer> allowStatusCodeList = Arrays.asList(waitUpCode, hasUpCode, waitDowmCode);


        String messageNotice = String.format("条码:【%s】的产品的状态是【%s】", sn, enumName);
        log.warn(messageNotice);

        if (!allowStatusCodeList.contains(goodsStatus)) {

            String message = String.format("条码:【%s】的产品的状态是【%s】不允许拣货", sn, enumName);
            if (Objects.equals(modeType, commonFlag)) {
                message = prefix + message;
            }
            Shift.fatal(StatusCode.BUSINESS_ERR, message);
        }

        //4）检查产品SN的批次属性，是否与发货单明细批次属性符合

        Long gdnDtlId = picktaskDtl.getDocDtlId();
        GdnDtl gdnDtl = gdnCoreService.getGdnDtlByGdnDtlId(gdnDtlId);

        //goods的批次号
        if (!lotno.equals(gdnDtl.getLotno())) {

//            String message = String.format("产品的状态是【%s】不允许拣货", enumName);
            String message = String.format("条码:【%s】的批次号与发货单明细的批次号不匹配", sn);
            if (Objects.equals(modeType, commonFlag)) {
                message = prefix + message;
            }
            Shift.fatal(StatusCode.BUSINESS_ERR, message);
        }
        //TODO 测试代码，测试和投产记得删除
//        int a = 0 / 0;
//        Shift.fatal("5555555555555");

        //3）检查产品SN的库区，是否预配的库存，不允许跨区拣货
        //根据发货单明细ID查询发货单预配表：WMS_GDN_PRE_ALOC
//        DocPreAloc gdnPreAloc = gdnCoreService.getGdnPreAlocByGdnId(gdnDtlId, commonFlag);
        DocPreAloc gdnPreAloc = docPreAlocService.selectByPk(picktaskDtl.getDocPreAlocId());

        //发货单对应的库区ID
        Integer areaId = gdnPreAloc.getZoneId();
        Integer whsId = gdnPreAloc.getWhsId();
        if (!whsId.equals(goods.getWhsId())) {

            String message = String.format("条码:【%s】该SN对应的仓库与拣货的仓库不匹配", sn);
            if (Objects.equals(modeType, commonFlag)) {
                message = prefix + message;
            }
            Shift.fatal(StatusCode.BUSINESS_ERR, message);
        }

        log.info("执行SN检查【结束】,SN:{}", sn);
    }


    /**
     * 查询拣货任务管理
     *
     * @param requestQueryer
     * @param servletRequest
     * @param servletResponse
     */
    public PagerInfo<List<PicktaskMangementResp>> getTaskMmanagementInfo
    (Queryer<PicktaskMangementRequest> requestQueryer, HttpServletRequest servletRequest, HttpServletResponse
            servletResponse) {

        PicktaskMangementRequest request = requestQueryer.getRequest();

        if (request.getShipmentDateTime() != null) {
            request.setShipmentDateTimeDate(new Date(request.getShipmentDateTime()));

            DateTime end = DateUtil.endOfDay(new Date(request.getShipmentDateTime()));
            request.setShipmentDateTimeDateEnd(end);
        }

        Pager<?> page = requestQueryer.getPage();

        // page为空，默认最大分页
        if (page == null) {
            page = new Pager<PicktaskMangementResp>();
            page.setSkipCount(0);
            page.setMaxResultCount(Integer.MAX_VALUE);
        }

        PagerInfo<List<PicktaskMangementResp>> info = new PagerInfo<>();


        try {
            Page<PicktaskMangementResp> _page = PageHelper.offsetPage(page.getSkipCount(), page.getMaxResultCount());
            PageInfo<PicktaskMangementResp> result = new PageInfo<>(_page);

            List<PicktaskMangementResp> picktaskMangementRespList = picktaskDtlService.getTaskMmanagementInfo(request);

            List<Integer> customerIdList = picktaskMangementRespList.stream().filter(s -> s.getCustomer() != null).map(s -> s.getCustomer()).distinct().collect(Collectors.toList());

            List<CustomerDto> customerList = basicGetCustomerApi.getCustomer(customerIdList);

            for (CustomerDto customerDto : customerList) {
                for (PicktaskMangementResp picktaskMangementResp : picktaskMangementRespList) {
                    if (customerDto.getCustomerId().equals(picktaskMangementResp.getCustomer())) {
                        picktaskMangementResp.setCustomerName(customerDto.getCustomerName());
                    }
                }
            }


            result.setList(picktaskMangementRespList);
            info.setTotalCount(_page.getTotal());// 总数
            info.setItems(result.getList());// 返回结果
        } catch (Exception ex) {
            ex.printStackTrace();
            Shift.fatal(StatusCode.SERVER_MODIFY_ERROR);
        }
        return info;
    }

    /**
     * 更新明细的任务领取人
     *
     * @param picktaskDtlId
     * @param username
     * @param servletRequest
     * @param servletResponse
     */
    public Result updateTaskReceiver(Integer picktaskDtlId, String username, HttpServletRequest
            servletRequest, HttpServletResponse servletResponse) {

        //拣货中和已完成任务不可改派任务

        int pickIngCode = PicktaskStatusEnum.PICK_ING.getCode();//拣货中
        int pickEdCode = PicktaskStatusEnum.PICK_ED.getCode();//已拣货,完成


        PicktaskDtl picktaskDtl = picktaskDtlService.selectByPicktaskDtlId(picktaskDtlId);
        if (picktaskDtl.getStatus().equals(pickEdCode) || picktaskDtl.getStatus().equals(pickIngCode)) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "拣货中和已完成的任务不可改派！");
        }
        String loginName = HttpSessionUtil.getLoginName(servletRequest);

        Integer row = picktaskDtlService.updateTaskReceiverOnPicktaskDtlId(picktaskDtlId, username, loginName, new Date());
        log.info("成功更新{}条记录", row);

        Result result = new Result();
        result.setData(1);
        return result;
    }


    /**
     * 查询改派任务的用户与仓库库区关联信息
     */
    public List<UserWhsZoneWorkDivisionResponse> getUserWhsZoneWorkDivision(Integer picktaskDtlId, String filter) {
        PicktaskDtl dtl = picktaskDtlService.selectByPicktaskDtlId(picktaskDtlId);
        DocPreAloc docPreAloc = docPreAlocMapper.selectById(dtl.getDocPreAlocId());

        GetUserWhsZoneWorkDivisionDto dto = new GetUserWhsZoneWorkDivisionDto();
        dto.setWorkDivisionType(WorkDivisionTypeEnum.WO.code);
        dto.setFactory(docPreAloc.getFactory());
        dto.setFilter(filter);
        if (docPreAloc.getVendorId() != null) {
            dto.setVendor(docPreAloc.getVendorId().toString());
        }
        return workDivisionCoreService.getUserWhsZoneWorkDivision(dto);
    }

    /**
     * 发货单生成拣货单
     *
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result<IssuePickingResponse> createPiacktaskJobFromGdn(Long gdnHeaderId,
                                                                  HttpServletRequest servletRequest) throws Exception {
        String loginName = HttpSessionUtil.getLoginName(servletRequest);
        Result<IssuePickingResponse> result = createPiacktaskJobFromGdn(gdnHeaderId, loginName);
        return result;
    }

    /**
     * 生成拣货任务
     *
     * @param gdnHeaderId
     * @param loginName
     * @return
     */
    public Result<IssuePickingResponse> createPiacktaskJobFromGdn(Long gdnHeaderId, String loginName) {

        GdnHeader gdnHeader = gdnCoreService.getHeadByHeadId(gdnHeaderId);
        Result<IssuePickingResponse> result = new Result();
        //过滤单据条件------------------------------------------------------------------------------------start

        //发货单类型
        String gdnType = gdnHeader.getGdnType();

        //获取发货单的单据类型
        List<DataItemDetailDTO> dataItemDetailDTOSList = basicDataItemDetailApi.getDataItemDetailByCode(gdn);

        if (dataItemDetailDTOSList.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "未查询到发货单的单据类型");
        }

        //根据header的发货单类型匹配
        List<DataItemDetailDTO> dataItemDetailDTOS = dataItemDetailDTOSList.stream().filter(s -> s.getItemValue().equals(gdnType)).collect(Collectors.toList());

        if (dataItemDetailDTOS.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "未查询到发货单的单据类型与之对应的类型");
        }

        DataItemDetailDTO dataItemDetailDTO = dataItemDetailDTOS.get(0);

        //获取数据字典的拓展属性:WMS-单据类型-出库单-数据项对应的扩展属性attr=need_pick_task,value=1
        DataitemAttrDto dataitemAttrDto = basicDataAttrApi.getDataitemAttrDtoByItemDtlIdAndAttr(dataItemDetailDTO.getDataItemDetailId(), gdnAttr, commonFlag.toString(), commonFlag);

        if (dataitemAttrDto == null) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "对应的发货单的单据类型的【扩展属性】不符合生成拣货要求");
        }


        //过滤单据条件--------------------------------------------------------------------------------------end
        List<GdnDtl> gdnDtlList = gdnCoreService.getGdnDtlByHeadId(gdnHeader.getGdnHeaderId());

        //已经拣货过的明细条数
        Integer pickedCount = Math.toIntExact(gdnDtlList.stream().map(s -> s.getPickTaskFlag()).filter(s -> Objects.equals(s, commonFlag)).count());
        if (pickedCount.equals(gdnDtlList.size())) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "该发货单下明细已全部生成拣货任务，拒绝重复生成");
        }


        gdnDtlList = gdnDtlList.stream().filter(s -> GdnDtlUdf1Enum.YES.code.equals(s.getUdf1())).collect(Collectors.toList());

        //仓库ID集合
        List<Integer> whsIdList = gdnDtlList.stream().map(s -> s.getWhsId()).distinct().collect(Collectors.toList());
        //过滤仓库条件------------------------------------------------start
        //仓库满足条件：仓库扩展属性attr=need_pick_task,value=1
        if (whsIdList.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "未分库单据不能执行下发操作");
        }

        //查数据字典获取：仓库扩展属性(Stock_Extend_Attribute)
        List<DataItemDetailDTO> dataItemDetailDTOList = basicDataItemDetailApi.getDataItemDetailByCode(warehouseCode);

        List<DataItemDetailDTO> dataItemDetailDTOsList = dataItemDetailDTOList.stream().filter(s -> s.getItemName().equals(picktaskKey)).collect(Collectors.toList());

        if (dataItemDetailDTOsList.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "未查询到符合条件的仓库扩展属性配置");
        }
        DataItemDetailDTO itemDetailDTO = dataItemDetailDTOsList.get(0);

        //根据仓库ID集合查询【仓库扩展属性】:ID,启用,仓库扩展属性attr=need_pick_task,value=1
        List<WarehouseAttr> warehouseAttrList = warehouseAttrService.getPicktaskAttrList(whsIdList, commonFlag, itemDetailDTO.getItemValue(), commonFlag.toString());
        if (warehouseAttrList.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "发货单明细的仓库存在【仓库拓展属性】未配置生成拣货任务的标识");
        }

        //符合条件的仓库ID集合
        List<Integer> conformWhsIdList = warehouseAttrList.stream().distinct().filter(s -> s.getWhsId() != null).map(s -> s.getWhsId()).collect(Collectors.toList());

        //过滤器符合仓库条件的明细
        gdnDtlList = gdnDtlList.stream().filter(s -> conformWhsIdList.contains(s.getWhsId())).collect(Collectors.toList());

        if (gdnDtlList.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "该单据中未查询到符合【下发条件】的仓库，请核实仓库信息");
        }

        //过滤仓库条件--------------------------------------------------end

        //c．发货单明细表/调拨单明细表中PICK_TASK_FLAG(生成拣货任务标识)≠1
//        gdnDtlList = gdnDtlList.stream().filter(s -> s.getPickTaskFlag() != commonFlag).collect(Collectors.toList());
        gdnDtlList = gdnDtlList.stream().filter(s -> Objects.equals(PicktaskFlagEnum.UN_CREATED.code, s.getPickTaskFlag())).collect(Collectors.toList());
        if (gdnDtlList.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "未查询到符合【下发条件】的发货单明细，请核实仓库信息或单据明细的下发标识");
        }
//        int a = 0 / 0;

        //一次性获取拣货任务生成规则
        //从仓库扩展属性取出属性attr=pick_rule的【值】，即拣货规则
        //保存 仓库ID和对应的拣货规则
        List<Integer> whsIdList1 = gdnDtlList.stream().map(s -> s.getWhsId()).distinct().collect(Collectors.toList());

        //先从数据字典取值：根据数据字典的【拣货规则】取数据
        List<DataItemDetailDTO> itemDetailDTOS = dataItemDetailDTOList.stream().filter(s -> s.getItemName().equals(pickRuleKey)).collect(Collectors.toList());

        if (itemDetailDTOS.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "仓库扩展属性未配置【拣货规则】");
        }
        DataItemDetailDTO detailDTO = itemDetailDTOS.get(0);

        //仓库扩展属性的值:【拣货规则】
        String itemValue = detailDTO.getItemValue();//pick_rule

        //仓库ID和对应的拣货规则
        List<WarehouseAttr> warehouseAttrs = warehouseAttrService.getByWhsIdListAndAttr(whsIdList1, itemValue, commonFlag);


//        int a = 0 / 0;
        //一次性根据规则获取所需辅助信息
        //a．若pick_rule=ByZone：通过用户-分工获取用户-库区的对照关系


        // 判断当前任务对应的拣货规则、辅助信息是否存在？
        //若拣货规则、辅助信息任一项不存在，则转向80，记录一笔失败日志

        //TODO 判断拣货规则+辅助信息存在？正常:抛异常出去
        if (warehouseAttrs.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "根据仓库未查询到【拣货规则】");
        }

        //获取预配信息：
        //根据订单明细ID从单据预配表(WMS_DOC_PRE_ALOC)获取预配信息
        //70.生成拣货任务
        //调用【拣货任务生成】服务，根据该订单明细分配的不同库区生成相应条数的拣货任务，并分配给库区对应的用户ID，同步更新发货明细或调拨单明细.pick_task_flag=1。

        Map<Integer, List<GdnDtl>> groupGdnList = gdnDtlList.stream().collect(Collectors.groupingBy(s -> s.getWhsId()));

        IssuePickingResponse issuePickingResponse = new IssuePickingResponse();
        List<IssuePickingItemResponse> items = new ArrayList<>();

        for (Map.Entry<Integer, List<GdnDtl>> entry : groupGdnList.entrySet()) {

            Integer whsId = entry.getKey();
            //拣货任务号
            String picktaskCode = basicSimpleCodeRuleApi.GenerateCode(this.picktaskCode);
            //生成拣货任务单头
            PicktaskHeader picktaskHeader = createNewPicktaskHeader(loginName, whsId, picktaskCode);

            //生成拣货任务明细
            List<GdnDtl> createGdnList = entry.getValue();
            //根据发货明细ID查询预配表
            List<Long> gdnDtlIdList = createGdnList.stream().map(s -> s.getGdnDtlId()).collect(Collectors.toList());
            List<DocPreAloc> docPreAlocList = docPreAlocService.selectByDocDtlIdList(gdnDtlIdList);
            //添加过滤，有效预配才生成
            docPreAlocList = docPreAlocList.stream().filter(s -> s.getStatus() == commonFlag).collect(Collectors.toList());

            Integer linNo = 1;
            //根据预配表生成拣货明细
            for (DocPreAloc docPreAloc : docPreAlocList) {

                //TODO 添加校验可用库存（库存数-预支-分配-冻结）
//                String udf1 = docPreAloc.getUdf1();//这个是库区库存ID
//                Integer invZoneId = Integer.valueOf(udf1);
//                InvZone invZone = invZoneService.selectByPk(invZoneId);
//                BigDecimal qty = invZone.getQty();
//                BigDecimal preOutQty = invZone.getPreOutQty();
//                BigDecimal alocQty = invZone.getAlocQty();
//                BigDecimal holdQty = invZone.getHoldQty();
//
//                BigDecimal subtract = qty.subtract(preOutQty).subtract(alocQty).subtract(holdQty);
//
//                if (subtract.compareTo(BigDecimal.ZERO) == -1) {
//                    result.setFlag(false);
//                    result.setMessage("可用库存不足,确认下发拣货任务？");
//                    return result;
//                }
                //TODO 添加校验可用库存（库存数-预支-分配-冻结）


                Integer rowAdd = addNewPickTaskDtl(loginName, picktaskHeader, linNo, docPreAloc);
                log.info("成功新增{}条记录", rowAdd);

                //前端出参
                GdnDtl gdnDtl = gdnCoreService.getGdnDtlByGdnDtlId(docPreAloc.getDocDtlId());
                IssuePickingItemResponse pickingItemResponse = new IssuePickingItemResponse();

                pickingItemResponse.setComments("拣货任务号:" + picktaskCode);
                pickingItemResponse.setGdnLineNo(gdnDtl.getGdnLineNo());
                pickingItemResponse.setResult(1);
                items.add(pickingItemResponse);
                //前端出参

                //同步更新发货明细或调拨单明细.pick_task_flag=1
                Integer rowUpdate = gdnCoreService.updateDtlStatusByDtlId(docPreAloc.getDocDtlId(), commonFlag, loginName);
                log.info("成功更新{}条记录", rowUpdate);

                linNo++;
            }
        }

        issuePickingResponse.setItems(items);
        issuePickingResponse.setSuccess(items.size());


        //设置失败次数
        long count = issuePickingResponse.getItems().stream().map(s -> s.getResult()).filter(s -> s.equals(commonFlag)).count();
        Integer success = issuePickingResponse.getSuccess();
        issuePickingResponse.setFail((int) (success - count));

        result.setData(issuePickingResponse);
        return result;
    }

    /**
     * 创建信息拣货任务明细
     *
     * @param loginName
     * @param picktaskHeader
     * @param linNo
     * @param docPreAloc
     * @return
     */
    public synchronized Integer addNewPickTaskDtl(String loginName, PicktaskHeader picktaskHeader, Integer
            linNo, DocPreAloc docPreAloc) {
        PicktaskDtl picktaskDtl = new PicktaskDtl();

        picktaskDtl.setPicktaskHeaderId(picktaskHeader.getPicktaskHeaderId());
        picktaskDtl.setPicktaskLineNo(linNo);

        picktaskDtl.setDocDtlId(docPreAloc.getDocDtlId());
        picktaskDtl.setDocType(docPreAloc.getDocType());

        picktaskDtl.setCreateTime(new Date());
        picktaskDtl.setCreateUser(loginName);
        picktaskDtl.setStatus(PicktaskStatusEnum.NO_PICK.getCode());
        picktaskDtl.setZoneId(docPreAloc.getZoneId());

        picktaskDtl.setDocPreAlocId(docPreAloc.getDocPreAlocId());//订单预配ID
        picktaskDtl.setPlanQty(docPreAloc.getPreAlocQty());//计划拣货数量
        picktaskDtl.setDispatchFlag(commonFlag);//任务下发标识
        picktaskDtl.setCmpQty(BigDecimal.ZERO);//设置初始【实际拣货数量】0

        // 设置任务领取人
        String taskReceiver = getTaskReceiver(docPreAloc);
        picktaskDtl.setTaskReceiver(taskReceiver);
        return picktaskDtlService.addOne(picktaskDtl);
    }

    /**
     * 设置任务领取人
     * @param docPreAloc
     * @return
     */
    private String getTaskReceiver(DocPreAloc docPreAloc) {
        List<UserWhsZoneWorkDivisionResponse> whsZoneWorkDivisionResponses = null;
        GetUserWhsZoneWorkDivisionDto workDivisionDto = new GetUserWhsZoneWorkDivisionDto();
        workDivisionDto.setWorkDivisionType(WorkDivisionTypeEnum.WO.code);
        workDivisionDto.setWhs(docPreAloc.getWhsId().toString());
        if (docPreAloc.getZoneId() != null) {
            workDivisionDto.setZone(docPreAloc.getZoneId().toString());
            whsZoneWorkDivisionResponses = workDivisionCoreService.getUserWhsZoneWorkDivision(workDivisionDto);
        }
        if (whsZoneWorkDivisionResponses == null || whsZoneWorkDivisionResponses.size() == 0) {
            workDivisionDto.setZone(null);
            whsZoneWorkDivisionResponses = workDivisionCoreService.getUserWhsZoneWorkDivision(workDivisionDto);
        }
        if (whsZoneWorkDivisionResponses.size() == 0) {
            Warehouse whs = warehouseService.selectById(docPreAloc.getWhsId());
            String message = String.format("未查询到仓库:【%s】,仓库代码:【%s】的仓管员信息，未能设置任务领取人", whs.getWhsName(), whs.getWhsCode());
            /*if (docPreAloc.getZoneId() != null) {
                Zone zone = zoneService.getById(docPreAloc.getZoneId());
                message = String.format("未查询到库区:【%s】,库区代码:【%s】的仓管员信息，未能设置任务领取人", zone.getZoneName(), zone.getZoneCode());
            } else {
                Warehouse whs = warehouseService.selectById(docPreAloc.getWhsId());
                message = String.format("未查询到仓库:【%s】,仓库代码:【%s】的仓管员信息，未能设置任务领取人", whs.getWhsName(), whs.getWhsCode());
            }*/
            Shift.fatal(StatusCode.BUSINESS_ERR, message);
        }


        String taskReceiver = new String();
        String split = ",";
        if (whsZoneWorkDivisionResponses.size() > 0) {
//            for (UserWhsZoneWorkDivisionResponse respons : whsZoneWorkDivisionResponses) {
//
//                taskReceiver = taskReceiver + respons.getUsername() + split;
//
//            }
            List<String> userNameList = whsZoneWorkDivisionResponses.stream().filter(s -> s.getUsername() != null).map(s -> s.getUsername()).distinct().collect(Collectors.toList());
            for (String name : userNameList) {
                taskReceiver = taskReceiver + name + split;
            }

        }
        //去除最后一个字符串
        taskReceiver = taskReceiver.substring(0, taskReceiver.length() - 1);
        return taskReceiver;
    }

    /**
     * 生成拣货任务单头
     *
     * @param loginName
     * @param whsId
     * @param picktaskCode
     * @return
     */
    public synchronized PicktaskHeader createNewPicktaskHeader(String loginName, Integer whsId, String
            picktaskCode) {
        PicktaskHeader picktaskHeader = new PicktaskHeader();

        picktaskHeader.setPicktaskNo(picktaskCode);

        picktaskHeader.setCreateUser(loginName);
        picktaskHeader.setCreateTime(new Date());

        picktaskHeader.setStatus(PicktaskStatusEnum.NO_PICK.getCode());//未拣货
        picktaskHeader.setWhsId(whsId);
        picktaskHeader.setTaskType(PicktaskTypeEnum.SINGLE.getCode());

        Integer row = picktaskHeaderService.addOne(picktaskHeader);
        log.info("成功新增{}条记录", row);
        return picktaskHeader;
    }

    /**
     * 取消拣货任务
     *
     * @param
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result cancelPicktask(List<Integer> picktaskDtlIdList, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        cancelPick(picktaskDtlIdList, servletRequest);

        return new Result();
    }

    /**
     * 取消拣货任务
     *
     * @param picktaskDtlIdList
     * @param servletRequest
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void cancelPick(List<Integer> picktaskDtlIdList, HttpServletRequest servletRequest) {
        for (Integer picktaskDtlId : picktaskDtlIdList) {

            PicktaskDtl picktaskDtl = picktaskDtlService.selectByPicktaskDtlId(picktaskDtlId);
            String loginName = HttpSessionUtil.getLoginName(servletRequest);

            int noPickCode = PicktaskStatusEnum.NO_PICK.getCode();//未拣货
            int cancelCode = PicktaskStatusEnum.CANCEL.getCode();//取消

            PicktaskHeader picktaskHeader = picktaskHeaderService.getByPk(picktaskDtl.getPicktaskHeaderId());

            //若不是未拣货的任务，不能取消
            if (!picktaskDtl.getStatus().equals(noPickCode)) {
                String msg =String.format("拣货任务号:【%s】下的任务明细行号:【%d】的单据状态非【未拣货】不允许取消",picktaskHeader.getPicktaskNo(),picktaskDtl.getPicktaskLineNo());
                Shift.fatal(StatusCode.BUSINESS_ERR, msg);
            }

            //取消【拣货任务】后，将其状态改为0
            Integer rowUpdate = picktaskDtlService.updateStatusByPk(picktaskDtlId, cancelCode, loginName, new Date());
            log.info("成功更新{}条记录", rowUpdate);


            //若同一单拣货任务【同一拣货任务号/单头ID】下所有拣货任务明细状态=0[取消]，则更新拣货任务状态=0[取消]
            Integer headerId = picktaskDtl.getPicktaskHeaderId();
            List<PicktaskDtl> picktaskDtlList = picktaskDtlService.getDtlByHeadId(headerId);

            //同一个拣货任务号状态为【取消】的条数
            long count = picktaskDtlList.stream().filter(s -> s.getStatus().equals(cancelCode)).count();
            Integer row = new Integer(String.valueOf(count));
            if (row.equals(picktaskDtlList.size())) {
                //明细全都为取消,则更新单头状态
                Integer rowHeaderUpdate = picktaskHeaderService.updateStatusByPK(headerId, cancelCode, loginName, new Date());
                log.info("成功更新{}条记录", rowHeaderUpdate);
            }

            //若同一单发货单明细或调拨单明细所对应的所有拣货任务明细状态=0[取消]，更新对应的发货单明细或调拨单明细 pick_task_flag=0；
            //否则更新对应的发货单明细或调拨单明细pick_task_flag=2[部分生成]
            Long docDtlId = picktaskDtl.getDocDtlId();
            String docType = picktaskDtl.getDocType();


            List<PicktaskDtl> picktaskDtls = picktaskDtlService.getByDocDtlIdAndDocType(docDtlId, docType);

            long cancelCount = picktaskDtls.stream().filter(s -> s.getStatus().equals(cancelCode)).count();
            Integer cancelRow = new Integer(String.valueOf(cancelCount));

            int partCreatedCode = PicktaskFlagEnum.PART_CREATED.getCode();//部分生成
            int alreadyCreatedCode = PicktaskFlagEnum.ALREADY_CREATED.getCode();//已生成
            int unCreatedCode = PicktaskFlagEnum.UN_CREATED.getCode();

            if (cancelRow.equals(picktaskDtls.size())) {
                //同一 发货单/调拨单 所有拣货任务明细都 = 0
                updateGdnTransferPicktaskFlag(loginName, docDtlId, docType, unCreatedCode);
            } else {
                updateGdnTransferPicktaskFlag(loginName, docDtlId, docType, partCreatedCode);
            }
        }
    }

    /**
     * 更新调拨单/发货单明细 的拣货状态
     *
     * @param loginName
     * @param docDtlId
     * @param docType
     * @param partCreatedCode
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public void updateGdnTransferPicktaskFlag(String loginName, Long docDtlId, String docType, int partCreatedCode) {

        if (docType.startsWith("D")) {
            //发货单逻辑
            Integer rowGdn = gdnCoreService.updateDtlStatusByDtlId(docDtlId, partCreatedCode, loginName);
            log.info("成功更新{}条记录", rowGdn);
        } else if (docType.startsWith("T")) {
            //调拨单逻辑
            Integer rowTran = transferService.updateDtlPicktaskFlag(docDtlId, partCreatedCode, loginName);
            log.info("成功更新{}条记录", rowTran);
        } else {
            Shift.fatal(StatusCode.BUSINESS_ERR, "拣货任务明细数据异常");
        }

    }

    /**
     * 关闭拣货任务
     *
     * @param picktaskDtlIdList
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result closePicktask(List<Integer> picktaskDtlIdList, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {


        for (Integer picktaskDtlId : picktaskDtlIdList) {


            /**
             * 1) 更新拣货任务明细状态=4[已关闭]
             *
             * 2) 若同一单拣货任务下所有拣货任务明细状态=4[已关闭]，则更新拣货任务状态=4[已关闭]
             */
            PicktaskDtl picktaskDtl = picktaskDtlService.selectByPicktaskDtlId(picktaskDtlId);
            String loginName = HttpSessionUtil.getLoginName(servletRequest);

            int pickIngCode = PicktaskStatusEnum.PICK_ING.getCode();//拣货中
            int closedCode = PicktaskStatusEnum.CLOSED.getCode();//已关闭

            if (!picktaskDtl.getStatus().equals(pickIngCode)) {
                Shift.fatal(StatusCode.BUSINESS_ERR, "非【拣货中】中状态的任务不允许关闭");
            }

            Integer row = picktaskDtlService.updateStatusByPk(picktaskDtlId, closedCode, loginName, new Date());
            log.info("成功更新{}条记录", row);


            Integer headerId = picktaskDtl.getPicktaskHeaderId();

            //查同一拣货任务下的明细
            List<PicktaskDtl> picktaskDtlList = picktaskDtlService.getDtlByHeadId(headerId);

            //同一拣货任务下，已关闭的条数
            Long closeCount = picktaskDtlList.stream().filter(s -> s.getStatus().equals(closedCode)).count();

            Integer closeCountInt = new Integer(String.valueOf(closeCount));

            if (closeCountInt.equals(picktaskDtlList.size())) {
                //已关闭条数=明细条数
                Integer rowHeader = picktaskHeaderService.updateStatusByPK(headerId, closedCode, loginName, new Date());
                log.info("成功更新{}条记录", rowHeader);
            }
        }

        //TODO
        return new Result();

    }

    /**
     * 下发前校验，前面 的校验来自于
     *
     * @param gdnHeaderId
     * @param servletRequest
     * @return
     */
    public Result createPicktaskBefore(Long gdnHeaderId, HttpServletRequest servletRequest) {

//        String loginName = HttpSessionUtil.getLoginName(servletRequest);
        GdnHeader gdnHeader = gdnCoreService.getHeadByHeadId(gdnHeaderId);
        Result result = new Result();
        //过滤单据条件------------------------------------------------------------------------------------start

        //发货单类型
        String gdnType = gdnHeader.getGdnType();

        //获取发货单的单据类型
        List<DataItemDetailDTO> dataItemDetailDTOSList = basicDataItemDetailApi.getDataItemDetailByCode(gdn);

        if (dataItemDetailDTOSList.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "未查询到发货单的单据类型");
        }

        //根据header的发货单类型匹配
        List<DataItemDetailDTO> dataItemDetailDTOS = dataItemDetailDTOSList.stream().filter(s -> s.getItemValue().equals(gdnType)).collect(Collectors.toList());

        if (dataItemDetailDTOS.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "未查询到发货单的单据类型与之对应的类型");
        }

        DataItemDetailDTO dataItemDetailDTO = dataItemDetailDTOS.get(0);

        //获取数据字典的拓展属性:WMS-单据类型-出库单-数据项对应的扩展属性attr=need_pick_task,value=1
        DataitemAttrDto dataitemAttrDto = basicDataAttrApi.getDataitemAttrDtoByItemDtlIdAndAttr(dataItemDetailDTO.getDataItemDetailId(), gdnAttr, commonFlag.toString(), commonFlag);

        if (dataitemAttrDto == null) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "对应的发货单的单据类型的【扩展属性】不符合生成拣货要求");
        }


        //过滤单据条件--------------------------------------------------------------------------------------end
        List<GdnDtl> gdnDtlList = gdnCoreService.getGdnDtlByHeadId(gdnHeader.getGdnHeaderId());

        //已经拣货过的明细条数
        Integer pickedCount = Math.toIntExact(gdnDtlList.stream().map(s -> s.getPickTaskFlag()).filter(s -> Objects.equals(s, commonFlag)).count());
        if (pickedCount.equals(gdnDtlList.size())) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "该发货单下明细已全部生成拣货任务，拒绝重复生成");
        }


        gdnDtlList = gdnDtlList.stream().filter(s -> GdnDtlUdf1Enum.YES.code.equals(s.getUdf1())).collect(Collectors.toList());

        //仓库ID集合
        List<Integer> whsIdList = gdnDtlList.stream().map(s -> s.getWhsId()).distinct().collect(Collectors.toList());
        //过滤仓库条件------------------------------------------------start
        //仓库满足条件：仓库扩展属性attr=need_pick_task,value=1
        if (whsIdList.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "未分库单据不能执行下发操作");
        }

        //查数据字典获取：仓库扩展属性(Stock_Extend_Attribute)
        List<DataItemDetailDTO> dataItemDetailDTOList = basicDataItemDetailApi.getDataItemDetailByCode(warehouseCode);

        List<DataItemDetailDTO> dataItemDetailDTOsList = dataItemDetailDTOList.stream().filter(s -> s.getItemName().equals(picktaskKey)).collect(Collectors.toList());

        if (dataItemDetailDTOsList.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "未查询到符合条件的仓库扩展属性配置");
        }
        DataItemDetailDTO itemDetailDTO = dataItemDetailDTOsList.get(0);

        //根据仓库ID集合查询【仓库扩展属性】:ID,启用,仓库扩展属性attr=need_pick_task,value=1
        List<WarehouseAttr> warehouseAttrList = warehouseAttrService.getPicktaskAttrList(whsIdList, commonFlag, itemDetailDTO.getItemValue(), commonFlag.toString());
        if (warehouseAttrList.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "发货单明细的仓库存在【仓库拓展属性】未配置生成拣货任务的标识");
        }

        //符合条件的仓库ID集合
        List<Integer> conformWhsIdList = warehouseAttrList.stream().distinct().filter(s -> s.getWhsId() != null).map(s -> s.getWhsId()).collect(Collectors.toList());

        //过滤器符合仓库条件的明细
        gdnDtlList = gdnDtlList.stream().filter(s -> conformWhsIdList.contains(s.getWhsId())).collect(Collectors.toList());

        if (gdnDtlList.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "该单据中未查询到符合【下发条件】的仓库，请核实仓库信息");
        }

        //过滤仓库条件--------------------------------------------------end

        //c．发货单明细表/调拨单明细表中PICK_TASK_FLAG(生成拣货任务标识)≠1
//        gdnDtlList = gdnDtlList.stream().filter(s -> s.getPickTaskFlag() != commonFlag).collect(Collectors.toList());
        gdnDtlList = gdnDtlList.stream().filter(s -> Objects.equals(PicktaskFlagEnum.UN_CREATED.code, s.getPickTaskFlag())).collect(Collectors.toList());
        if (gdnDtlList.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "未查询到符合【下发条件】的发货单明细，请核实仓库信息或单据明细的下发标识");
        }
//        int a = 0 / 0;

        //一次性获取拣货任务生成规则
        //从仓库扩展属性取出属性attr=pick_rule的【值】，即拣货规则
        //保存 仓库ID和对应的拣货规则
        List<Integer> whsIdList1 = gdnDtlList.stream().map(s -> s.getWhsId()).distinct().collect(Collectors.toList());

        //先从数据字典取值：根据数据字典的【拣货规则】取数据
        List<DataItemDetailDTO> itemDetailDTOS = dataItemDetailDTOList.stream().filter(s -> s.getItemName().equals(pickRuleKey)).collect(Collectors.toList());

        if (itemDetailDTOS.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "仓库扩展属性未配置【拣货规则】");
        }
        DataItemDetailDTO detailDTO = itemDetailDTOS.get(0);

        //仓库扩展属性的值:【拣货规则】
        String itemValue = detailDTO.getItemValue();//pick_rule

        //仓库ID和对应的拣货规则
        List<WarehouseAttr> warehouseAttrs = warehouseAttrService.getByWhsIdListAndAttr(whsIdList1, itemValue, commonFlag);


//        int a = 0 / 0;
        //一次性根据规则获取所需辅助信息
        //a．若pick_rule=ByZone：通过用户-分工获取用户-库区的对照关系


        // 判断当前任务对应的拣货规则、辅助信息是否存在？
        //若拣货规则、辅助信息任一项不存在，则转向80，记录一笔失败日志

        //TODO 判断拣货规则+辅助信息存在？正常:抛异常出去
        if (warehouseAttrs.size() == 0) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "根据仓库未查询到【拣货规则】");
        }

        //获取预配信息：
        //根据订单明细ID从单据预配表(WMS_DOC_PRE_ALOC)获取预配信息
        //70.生成拣货任务
        //调用【拣货任务生成】服务，根据该订单明细分配的不同库区生成相应条数的拣货任务，并分配给库区对应的用户ID，同步更新发货明细或调拨单明细.pick_task_flag=1。
        Map<Integer, List<GdnDtl>> groupGdnList = gdnDtlList.stream().collect(Collectors.groupingBy(s -> s.getWhsId()));

        List<IssuePickingItemResponse> items = new ArrayList<>();

        for (Map.Entry<Integer, List<GdnDtl>> entry : groupGdnList.entrySet()) {

//            Integer whsId = entry.getKey();
            //拣货任务号
//            String picktaskCode = basicSimpleCodeRuleApi.GenerateCode(this.picktaskCode);
            //生成拣货任务单头
//            PicktaskHeader picktaskHeader = createNewPicktaskHeader(loginName, whsId, picktaskCode);

            //生成拣货任务明细
            List<GdnDtl> createGdnList = entry.getValue();
            //根据发货明细ID查询预配表
            List<Long> gdnDtlIdList = createGdnList.stream().map(s -> s.getGdnDtlId()).collect(Collectors.toList());
            List<DocPreAloc> docPreAlocList = docPreAlocService.selectByDocDtlIdList(gdnDtlIdList);
            //添加过滤，有效预配才生成
            docPreAlocList = docPreAlocList.stream().filter(s -> s.getStatus() == commonFlag).collect(Collectors.toList());

//            Integer linNo = 1;
            //根据预配表生成拣货明细
            for (DocPreAloc docPreAloc : docPreAlocList) {

                //TODO 添加校验可用库存（库存数-预支-分配-冻结）
                String udf1 = docPreAloc.getUdf1();//这个是库区库存ID
                Integer invZoneId = Integer.valueOf(udf1);
                InvZone invZone = invZoneService.selectByPk(invZoneId);
                BigDecimal qty = invZone.getQty();
                BigDecimal preOutQty = invZone.getPreOutQty();
                BigDecimal alocQty = invZone.getAlocQty();
                BigDecimal holdQty = invZone.getHoldQty();

                BigDecimal preAlocQty = docPreAloc.getPreAlocQty();//预配表的分配数量
                BigDecimal subtract = qty.subtract(preOutQty).subtract(alocQty).subtract(holdQty).subtract(preAlocQty);

                if (subtract.compareTo(BigDecimal.ZERO) == -1) {
                    result.setFlag(false);
                    result.setMessage("可用库存不足,确认下发拣货任务？");
                    return result;
                }
                //TODO 添加校验可用库存（库存数-预支-分配-冻结）
//                linNo++;
            }
        }
        return result;
    }

    /**
     * 取消 拣货——扫码
     *
     * @param sn
     * @param scanMode:1,     整板扫描.0, 单台扫描
     * @param servletRequest
     * @param servletResponse
     * @return
     */
    public Result<List<PicktaskCancelResp>> scanCancelPickSn(String sn, Integer scanMode, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        Integer selectPalletCode = SnScanTypeEnum.SELECT_PALLET.getCode();//整板
        Integer selectSnCode = SnScanTypeEnum.SELECT_SN.getCode();//单台

        Goods goods = goodsService.selectBySn(sn);
        if (goods == null) {
            Shift.fatal(StatusCode.BUSINESS_ERR, "未查询到相关条码信息");
        }

        List<SnInforDto> snInforDtoList = new ArrayList<>();
        if (scanMode.equals(selectSnCode)) {
            //单台
            snInforDtoList = goodsService.selectSnInfosBySn(sn, SnScanTypeEnum.SELECT_SN);
        } else {
            //整板
            snInforDtoList = goodsService.selectSnInfosBySn(sn, SnScanTypeEnum.SELECT_PALLET);
        }


        int hasDownCode = GoodsStatusEnum.HAS_DOWN.getCode();//goods状态——已拣选

        //校验条码状态是否为【已拣选】状态
        for (SnInforDto snInforDto : snInforDtoList) {
            if (!snInforDto.getStatus().equals(hasDownCode)) {
                String msg = String.format("SN:【%s】状态不为【已拣选】，不可执行【取消拣货】", snInforDto.getGoodsSn());
                if (scanMode.equals(selectPalletCode)) {
                    //栈板扫描添加提示
                    msg = "同一栈板下的" + msg;
                }
                Shift.fatal(StatusCode.BUSINESS_ERR, msg);
            }
        }

        List<PicktaskCancelResp> picktaskCancelRespList = new ArrayList<>();
        for (SnInforDto snInforDto : snInforDtoList) {
            //返回信息
            PicktaskCancelResp picktaskCancelResp = new PicktaskCancelResp();
            BeanUtils.copyProperties(snInforDto, picktaskCancelResp);

            String goodsSn = snInforDto.getGoodsSn();
            PicktaskDtlGoods picktaskDtlGoods = picktaskDtlGoodsService.selectLastByGoodsSn(goodsSn);


            if (picktaskDtlGoods == null) {
                String msg = String.format("SN:【%s】未进行拣货操作，录入失败", snInforDto.getGoodsSn());
                if (scanMode.equals(selectPalletCode)) {
                    //栈板扫描添加提示
                    msg = "同一栈板下的" + msg;
                }
                Shift.fatal(StatusCode.BUSINESS_ERR, msg);
            }

            //拣货任务明细ID
            Integer picktaskDtlId = picktaskDtlGoods.getPicktaskDtlId();
            picktaskCancelResp.setPicktaskDtlId(picktaskDtlId);

            PicktaskDtl picktaskDtl = picktaskDtlService.selectByPicktaskDtlId(picktaskDtlId);
            //拣货任务行号
            picktaskCancelResp.setPicktaskLineNo(picktaskDtl.getPicktaskLineNo());
            //需求数
            picktaskCancelResp.setPlanQty(picktaskDtl.getPlanQty());
            //已拣数
            picktaskCancelResp.setCmpQty(picktaskDtl.getCmpQty());

            Integer zoneId = picktaskDtl.getZoneId();
            if (zoneId != null) {
                Zone zone = zoneService.getById(zoneId);
                //库区
                picktaskCancelResp.setZone(zone.getZoneName());
            }
            //拣货明细状态
            Integer status = picktaskDtl.getStatus();
            PicktaskStatusEnum picktaskStatusEnum = PicktaskStatusEnum.getEnumByCode(status);
            picktaskCancelResp.setPicktaskStatus(picktaskStatusEnum.getEnumName());

            PicktaskHeader picktaskHeader = picktaskHeaderService.getByPk(picktaskDtl.getPicktaskHeaderId());
            //拣货任务号
            picktaskCancelResp.setPicktaskNo(picktaskHeader.getPicktaskNo());
            //仓库
            Integer whsId = picktaskHeader.getWhsId();
            Warehouse warehouse = warehouseService.selectByPK(whsId);
            picktaskCancelResp.setWarehouse(warehouse.getWhsName());

            Long docDtlId = picktaskDtl.getDocDtlId();//发货单明细ID
            GdnDtl gdnDtl = gdnCoreService.getGdnDtlByGdnDtlId(docDtlId);
            //发货单行号
            picktaskCancelResp.setGdnLineNo(gdnDtl.getGdnLineNo());

            GdnHeader gdnHeader = gdnCoreService.getHeadByHeadId(gdnDtl.getGdnHeaderId());
            picktaskCancelResp.setGdnNo(gdnHeader.getGdnNo());
            picktaskCancelResp.setCntrNo(gdnHeader.getCntrNo());

            Sku sku = skuService.getSkuByPk(snInforDto.getSkuId());
            picktaskCancelResp.setDescCn(sku.getDescCn());
            picktaskCancelResp.setDescEn(sku.getDescEn());

            picktaskCancelRespList.add(picktaskCancelResp);
        }

        //    /**
        //     * 仓库
        //     */
        //    @ApiModelProperty("仓库")
        //    private String warehouse;

        Result<List<PicktaskCancelResp>> result = new Result<>();
        result.setData(picktaskCancelRespList);

        return result;
    }

    /**
     * PDA取消拣货任务
     *
     * @param cancelPicktaskRequest
     * @param servletRequest
     * @param servletResponse
     * 1）更新产品SN状态 = 已下架(4)
     * 2）更新产品SN对应的库位库存表：【分配数】减少
     * 3）更新产品SN对应的库区库存表：【分配数】减少
     * 4）从拣货任务明细货品清单(WMS_PICKTASK_DTL_GOODS)中删除对应的SN信息
     * 5）数量&状态更新
     * ①拣货任务明细表(WMS_PICKTASK_DTL)实际拣货数(CMP_QTY)减少；对应的发货单明细(WMS_GDN_DTL)拣货数量(PICK_QTY)减少。
     * ②对应的发货单明细(WMS_GDN_DTL)，若发货数量(ship_qty)=0并且拣货数量(PICK_QTY)<订单数量(ORDER_QTY) 时，状态(STATUS)更新为 拣货中(4)
     * 若发货数量(ship_qty)=0并且拣货数量(PICK_QTY)=0 时，状态(STATUS)更新为 待拣货(3)
     * ③拣货任务明细表(WMS_PICKTASK_DTL)：
     * 更新了，见文档
     * 写SN操作日志(wms_goods_log),为每个取消装柜的SN记录操作日志，操作代码“UnPick”
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result cancelPicktaskPda(CancelPicktaskRequest cancelPicktaskRequest, HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws Exception {
        RLock rLock = redisson.getLock("cancelPickWMS");

        try {
            boolean res = rLock.tryLock(2, 8, TimeUnit.SECONDS);
            if(res){
                String loginName = HttpSessionUtil.getLoginName(servletRequest);
                List<CancelPicktaskDtlRequest> cancelPicktaskDtlRequestList = cancelPicktaskRequest.getCancelPicktaskDtlRequestList();

                List<String> goodsSnList = cancelPicktaskDtlRequestList.stream().map(s -> s.getGoodsSn()).collect(Collectors.toList());

                //写goods的操作日志
                //先查原来的状态
                List<Goods> goodsList = goodsService.selectListByListSn(goodsSnList);
                for(Goods goods : goodsList){
                    if (!goods.getStatus().equals(GoodsStatusEnum.HAS_DOWN.code)) {
                        Shift.fatal("SN[" + goods.getGoodsSn() + "]状态非[" + GoodsStatusEnum.HAS_DOWN.enumName + "],不能取消拣选!");
                    }
                }

                int waitDowmCode = GoodsStatusEnum.WAIT_DOWM.getCode();//已下架
                //1）更新产品SN状态 = 已下架(4)
                Integer row = goodsService.updateStatusByGoodsList(waitDowmCode, goodsSnList, loginName);


                //从拣货任务明细货品清单(WMS_PICKTASK_DTL_GOODS)中删除对应的SN信息
                Integer deleteRow = picktaskDtlGoodsService.deleteBySnList(goodsSnList);
                log.info("删除{}条记录", deleteRow);

                Map<String, List<CancelPicktaskDtlRequest>> map = cancelPicktaskDtlRequestList.stream().collect(Collectors.groupingBy(s -> s.getGoodsSn()));
                for (Map.Entry<String, List<CancelPicktaskDtlRequest>> entry : map.entrySet()) {
                    CancelPicktaskDtlRequest cancelPicktaskDtlRequest = entry.getValue().get(0);

                    Goods goods = goodsService.selectBySn(cancelPicktaskDtlRequest.getGoodsSn());

                    //更新库位【分配数】
                    InvLoc invLoc = invLocService.selectByGoods(goods);
                    UpdateInventoryInputDto updateLocInventory = new UpdateInventoryInputDto();
                    BeanUtils.copyProperties(invLoc, updateLocInventory);

                    updateLocInventory.setAlocQty(goods.getQty().negate());
                    updateLocInventory.setQty(BigDecimal.ZERO);
                    updateLocInventory.setHoldQty(BigDecimal.ZERO);
                    updateLocInventory.setPreAlocQty(BigDecimal.ZERO);
                    updateLocInventory.setPreOutQty(BigDecimal.ZERO);
                    updateLocInventory.setPreInQty(BigDecimal.ZERO);
                    updateLocInventory.setOperator(loginName);
                    updateLocInventory.setLotNo(invLoc.getLotno());

                    //            Integer updateLocInventroy = inventoryService.updateLocInventroy(updateLocInventory);
                    //            inventoryService.invQtyValidation(updateLocInventory, 0);
                    inventoryService.updateLocInventroy(updateLocInventory);


                    //更新库区【分配数】
                    LocationResponse location = locationService.getLocation(goods.getLocId());

                    Integer zoneId = null;
                    if (location != null) {
                        zoneId = location.getZoneId();//库区ID
                    }
                    InvZone invZone = invZoneService.getEffectiveInvLoc(goods, zoneId);

                    UpdateInventoryInputDto updateZoneInventory = new UpdateInventoryInputDto();
                    BeanUtils.copyProperties(invZone, updateZoneInventory);

                    updateZoneInventory.setAlocQty(goods.getQty().negate());
                    updateZoneInventory.setQty(BigDecimal.ZERO);
                    updateZoneInventory.setHoldQty(BigDecimal.ZERO);
                    updateZoneInventory.setPreAlocQty(BigDecimal.ZERO);
                    updateZoneInventory.setPreOutQty(BigDecimal.ZERO);
                    updateZoneInventory.setPreInQty(BigDecimal.ZERO);
                    updateZoneInventory.setOperator(loginName);
                    updateZoneInventory.setLotNo(invZone.getLotno());


                    //            Integer updateZoneInventoryRow = inventoryService.updateZoneInventory(updateZoneInventory);
                    //            inventoryService.invQtyValidation(updateZoneInventory, 1);
                    inventoryService.updateZoneInventory(updateZoneInventory);

                    //①拣货任务明细表(WMS_PICKTASK_DTL)实际拣货数(CMP_QTY)减少；对应的发货单明细(WMS_GDN_DTL)拣货数量(PICK_QTY)减少。
                    PicktaskDtl picktaskDtl = picktaskDtlService.selectByPicktaskDtlId(cancelPicktaskDtlRequest.getDtlId());
                    //拣货任务明细表

                    //TODO 更新数量
                    //            BigDecimal cmpQty = picktaskDtl.getCmpQty();
                    PicktaskDtl picktaskDtlUpdate = new PicktaskDtl();
                    picktaskDtlUpdate.setPicktaskDtlId(cancelPicktaskDtlRequest.getDtlId());

                    //            picktaskDtlUpdate.setCmpQty(cmpQty.subtract(goods.getQty()));
                    picktaskDtlUpdate.setModifyTime(new Date());
                    picktaskDtlUpdate.setModifyUser(loginName);

                    //            Integer rowUpdate = picktaskDtlService.updateCmpQtyByPk(picktaskDtlUpdate);
                    Integer rowUpdate = picktaskDtlService.updateCmpQtyByDtlId(picktaskDtlUpdate,goods.getQty().negate());
                    if (rowUpdate == 0) {
                        Shift.fatal(StatusCode.OPERATE_ERR,"录入的实际拣货数量不能超出本明细计划拣货数量");
                    }

                    log.info("成功更新{}条记录", rowUpdate);
                    //发货单明细
                    GdnDtl gdnDtl = gdnCoreService.getGdnDtlByGdnDtlId(picktaskDtl.getDocDtlId());
                    BigDecimal pickQty = gdnDtl.getPickQty();

                    GdnDtl gdnDtlUpdate = new GdnDtl();
                    gdnDtlUpdate.setGdnDtlId(gdnDtl.getGdnDtlId());
                    //            gdnDtlUpdate.setPickQty(pickQty.subtract(goods.getQty()));
                    //            Integer gdnDtlUpdateRow = gdnCoreService.updateGdnDtlPickQty(gdnDtlUpdate.getGdnDtlId(), gdnDtlUpdate.getPickQty(), loginName);
                    Integer gdnDtlUpdateRow = gdnCoreService.updateGdnDtlPickQty2(gdnDtlUpdate.getGdnDtlId(), goods.getQty().negate(), loginName);

                    gdnDtlUpdate = gdnCoreService.getGdnDtlByGdnDtlId(picktaskDtl.getDocDtlId());

                    log.info("成功更新{}条记录", gdnDtlUpdateRow);

                    //②对应的发货单明细(WMS_GDN_DTL)，若发货数量(ship_qty)=0并且拣货数量(PICK_QTY)<订单数量(ORDER_QTY) 时，状态(STATUS)更新为 拣货中(4)
                    //若发货数量(ship_qty)=0并且拣货数量(PICK_QTY)=0 时，状态(STATUS)更新为 待拣货(3)

                    //发货数量(ship_qty)=0
                    boolean flagShip = gdnDtl.getShipQty().compareTo(BigDecimal.ZERO) == 0;

                    GdnHeader gdnHeader = gdnHeaderMapper.selectOne(new QueryWrapper<GdnHeader>().eq("GDN_HEADER_ID", gdnDtlUpdate.getGdnHeaderId()));
                    if (!(gdnHeader.getStatus().equals(GdnHeaderStatusEnum.FINISH.code)||gdnHeader.getStatus().equals(GdnHeaderStatusEnum.CLOSE.code))){

                    }


                    //拣货数量(PICK_QTY)=0
                    boolean flagZero = gdnDtlUpdate.getPickQty().compareTo(BigDecimal.ZERO) == 0;
                    if (flagShip && flagZero) {
                        GdnDtl gdnDtlStatus = new GdnDtl();
                        gdnDtlStatus.setGdnDtlId(gdnDtl.getGdnDtlId());
                        gdnDtlStatus.setStatus(GdnDtlStatusEnum.WAITPICK.getCode());
                        gdnDtlStatus.setModifyUser(loginName);
                        gdnDtlStatus.setModifyTime(new Date());

                        log.info("发货明细:{}", gdnDtlStatus.toString());
                        Integer gdnDtlStatusRow = gdnCoreService.updateGdnDtlStatusByPk(gdnDtlStatus.getGdnDtlId(), gdnDtlStatus.getStatus(), gdnDtlStatus.getModifyUser(), gdnDtlStatus.getModifyTime());
                        log.info("成功更新{}条记录", gdnDtlStatusRow);
                    }

                }

                Map<Integer, List<CancelPicktaskDtlRequest>> dtlIdMap = cancelPicktaskDtlRequestList.stream().collect(Collectors.groupingBy(s -> s.getDtlId()));

                for (Map.Entry<Integer, List<CancelPicktaskDtlRequest>> entry : dtlIdMap.entrySet()) {
                    Integer key = entry.getKey();//DTL_ID

                    //③拣货任务明细表(WMS_PICKTASK_DTL)：
                    //a.若已拣货数量(CMP_QTY)=0，则更新状态=未拣货(1)；
                    //b.若已拣货数量(CMP_QTY)>0并且<计划数量(PLAN_QTY)，则更新状态=拣货中(2)；
                    //c.若【取消拣货任务】开关为开启状态，则调用【取消拣货任务】服务(见1.2[拣货任务管理]2.4.2节)，更新状态=取消(0)；

                    //            PicktaskDtl picktaskDtlCmp = picktaskDtlService.selectByPicktaskDtlId(cancelPicktaskDtlRequest.getDtlId());
                    PicktaskDtl picktaskDtlCmp = picktaskDtlService.selectByPicktaskDtlId(key);

                    boolean cmpZeroFlag = picktaskDtlCmp.getCmpQty().compareTo(BigDecimal.ZERO) == 0;
                    boolean cmpOneFlag = picktaskDtlCmp.getCmpQty().compareTo(BigDecimal.ZERO) == 1;
                    boolean dtlPlanFlag = picktaskDtlCmp.getCmpQty().compareTo(picktaskDtlCmp.getPlanQty()) == -1;

                    if (cmpZeroFlag) {
                        int noPickCode = PicktaskStatusEnum.NO_PICK.getCode();
                        //                picktaskDtlService.updateStatusByPk(cancelPicktaskDtlRequest.getDtlId(), noPickCode, loginName, new Date());
                        picktaskDtlService.updateStatusByPk(key, noPickCode, loginName, new Date());
                    }

                    if (cmpOneFlag && dtlPlanFlag) {
                        int pickIngCode = PicktaskStatusEnum.PICK_ING.getCode();//拣货中
                        //                picktaskDtlService.updateStatusByPk(cancelPicktaskDtlRequest.getDtlId(), pickIngCode, loginName, new Date());
                        picktaskDtlService.updateStatusByPk(key, pickIngCode, loginName, new Date());
                    }

                    if (cancelPicktaskRequest.getCancelPicktaskFlag()) {
                        List<Integer> picktaskDtlList = new ArrayList<>();
                        //                picktaskDtlList.add(cancelPicktaskDtlRequest.getDtlId());
                        picktaskDtlList.add(key);
                        cancelPick(picktaskDtlList, servletRequest);
                    }
                }

                //④拣货任务主表(WMS_PICKTASK_HEADER)：
                //若一张拣货任务下全部拣货任务明细状态(STATUS) =未拣货(1)，更新拣货任务主表的状态(STATUS) =未拣货(1)，否则更新拣货任务表头的状态=拣货中(2)；
                List<Integer> dtlIdList = cancelPicktaskDtlRequestList.stream().map(s -> s.getDtlId()).distinct().collect(Collectors.toList());
                List<PicktaskDtl> picktaskDtlList = picktaskDtlService.selectByPicktaskDtlIdList(dtlIdList);
                List<Integer> headerIdList = picktaskDtlList.stream().map(s -> s.getPicktaskHeaderId()).distinct().collect(Collectors.toList());
                List<PicktaskHeader> picktaskHeaderList = picktaskHeaderService.selectByHeadIdList(headerIdList);

                int noPickCode = PicktaskStatusEnum.NO_PICK.getCode();
                int pickIngCode = PicktaskStatusEnum.PICK_ING.getCode();
                int cancelCode = PicktaskStatusEnum.CANCEL.getCode();


                for (PicktaskHeader picktaskHeader : picktaskHeaderList) {
                    List<PicktaskDtl> picktaskDtls = picktaskDtlService.getDtlByHeadId(picktaskHeader.getPicktaskHeaderId());

                    int picktaskDtlListSize = picktaskDtls.size();

                    //同一单头下所有明细的状态为【未拣货】的数量
                    Long countNoPick = picktaskDtls.stream().filter(s -> s.getStatus().equals(noPickCode)).count();
                    //同一单头下所有明细的状态为【取消】的数量
                    Long countCancel = picktaskDtls.stream().filter(s -> s.getStatus().equals(cancelCode)).count();


                    if (countNoPick.intValue() == picktaskDtlListSize) {
                        Integer rowUpdate = picktaskHeaderService.updateStatusByPK(picktaskHeader.getPicktaskHeaderId(), noPickCode, loginName, new Date());
                        log.info("成功更新{}条记录,{}的拣货单头状态为:{}", rowUpdate, picktaskHeader.getPicktaskNo(), noPickCode);

                    } else if(countCancel.intValue() == picktaskDtlListSize){
                        Integer rowUpdate = picktaskHeaderService.updateStatusByPK(picktaskHeader.getPicktaskHeaderId(), cancelCode, loginName, new Date());
                        log.info("成功更新{}条记录,{}的拣货单头状态为:{}", rowUpdate, picktaskHeader.getPicktaskNo(), cancelCode);

                    } else {
                        Integer rowUpdate = picktaskHeaderService.updateStatusByPK(picktaskHeader.getPicktaskHeaderId(), pickIngCode, loginName, new Date());
                        log.info("成功更新{}条记录,{}的拣货单头状态为:{}", rowUpdate, picktaskHeader.getPicktaskNo(), pickIngCode);

                    }

                }

                //拆包
                List<CancelPicktaskDtlRequest> dtlRequestSingleList = cancelPicktaskDtlRequestList.stream().filter(s -> s.getScanMode().equals(0)).collect(Collectors.toList());

                for (CancelPicktaskDtlRequest dtlRequest : dtlRequestSingleList) {
                    Goods goods = goodsService.selectBySn(dtlRequest.getGoodsSn());

                    Goods goodsUpdate = new Goods();
                    goodsUpdate.setGoodsId(goods.getGoodsId());
                    goodsUpdate.setModifyTime(new Date());
                    goodsUpdate.setModifyUser(loginName);

                    goodsService.updateGoodsDePackage(goodsUpdate);
                }

                for (CancelPicktaskDtlRequest request : cancelPicktaskDtlRequestList) {

                    Goods goods = goodsService.selectBySn(request.getGoodsSn());

                    //6）若SN库位不为空，则：
                    //①更新产品SN对应的库位库存表：扣减SN所在库位库存，增加空库位库存
                    //②更新产品SN对应的库区库存表：扣减SN所在库区库存，增加空库区库存
                    //③更新wms_goods表loc_id=null
                    this.repertoryReset(loginName, goods);
                }

                //写goods的操作日志
                //先查原来的状态
                List<Goods> goodsNewList = goodsService.selectListByListSn(goodsSnList);
                for (Goods goods : goodsList) {

                    for (Goods goodsNew : goodsNewList) {
                        if (goods.getGoodsSn().equals(goodsNew.getGoodsSn())) {
                            //

                            GoodsLog goodsLog = new GoodsLog();
                            goodsLog.setOpCode(GoodsLogEnum.UN_PICK.getCode());
                            goodsLog.setReason(GoodsLogEnum.UN_PICK.getEnumName());

                            goodsLog.setGoodsSn(goods.getGoodsSn());
                            goodsLog.setOperator(loginName);
                            goodsLog.setCreateUser(loginName);
                            goodsLog.setCreateTime(new Date());

                            Goods goodsCopy = new Goods();
                            goodsCopy.setGoodsId(goodsNew.getGoodsId());
                            goodsCopy.setModifyUser(goodsNew.getModifyUser());
                            goodsCopy.setModifyTime(goodsNew.getModifyTime());
                            goodsCopy.setStatus(goodsNew.getStatus());


                            String updateContent = new String();
                            String goodsJson = JSON.toJSONString(goods);

                            String goodsCopyJson = JSON.toJSONString(goodsCopy);
                            updateContent = "未更新的:" + goodsJson + ",更新后的:" + goodsCopyJson;
                            goodsLog.setUpdateContent(updateContent);

                            List<CancelPicktaskDtlRequest> picktaskDtlRequestList = map.get(goodsNew.getGoodsSn());
                            CancelPicktaskDtlRequest cancelPicktaskDtlRequest = picktaskDtlRequestList.get(0);

                            String comments = String.format("从PICKTASK_DTL_ID:【%s】取消拣货", cancelPicktaskDtlRequest.getDtlId());
                            goodsLog.setComments(comments);

                            int insertRow = goodsLogMapper.insert(goodsLog);
                        }
                    }
                }
                return new Result();
            }else {
                return new Result(false,"请勿重复提交");
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rLock.unlock();
        }
        //TODO 测试代码，测试和投产记得删除
//        int a = 0 / 0;
       return null;
    }

    /**
     * 取消拣货后，重置库存
     * @param loginName
     * @param goods
     * @throws Exception
     */
    private void repertoryReset(String loginName, Goods goods) throws Exception {
        if (goods.getLocId()!=null) {
            Integer tempLocId = goods.getLocId();//临时记录原来的库位ID

            Goods goodsUpdateLoc = new Goods();

            goodsUpdateLoc.setModifyUser(loginName);
            goodsUpdateLoc.setModifyTime(new Date());
            goodsUpdateLoc.setGoodsId(goods.getGoodsId());
            goodsUpdateLoc.setLocId(null);

            Integer locNull = goodsService.updateGoodsSetLocNull(goodsUpdateLoc);
            log.info("LOC_ID不为NULL置空，成功更新{}条记录",locNull);

            //扣减SN所在库位库存
            UpdateInventoryInputDto updateLocInventoryInputDtoLoc = new UpdateInventoryInputDto();
            updateLocInventoryInputDtoLoc.setWhsId(goods.getWhsId());
            updateLocInventoryInputDtoLoc.setLocId(tempLocId);
            updateLocInventoryInputDtoLoc.setOwner(goods.getOwner());
            updateLocInventoryInputDtoLoc.setSkuId(goods.getSkuId());
            updateLocInventoryInputDtoLoc.setFactory(goods.getFactory());
            updateLocInventoryInputDtoLoc.setVendorId(goods.getVendorId());
            updateLocInventoryInputDtoLoc.setLotNo(goods.getLotno());
            updateLocInventoryInputDtoLoc.setInvAttr(goods.getInvAttr());
            updateLocInventoryInputDtoLoc.setQaStatus(goods.getQaStatus());
            updateLocInventoryInputDtoLoc.setUnit(goods.getUnit());
            updateLocInventoryInputDtoLoc.setQty(goods.getQty().negate());//取负数
            updateLocInventoryInputDtoLoc.setOperator(loginName);

            inventoryService.updateLocInventroy(updateLocInventoryInputDtoLoc);

            //增加空库位库存
            UpdateInventoryInputDto updateLocInventoryInputDtoLocNull = new UpdateInventoryInputDto();
            updateLocInventoryInputDtoLocNull.setWhsId(goods.getWhsId());
            updateLocInventoryInputDtoLocNull.setLocId(null);
            updateLocInventoryInputDtoLocNull.setOwner(goods.getOwner());
            updateLocInventoryInputDtoLocNull.setSkuId(goods.getSkuId());
            updateLocInventoryInputDtoLocNull.setFactory(goods.getFactory());
            updateLocInventoryInputDtoLocNull.setVendorId(goods.getVendorId());
            updateLocInventoryInputDtoLocNull.setLotNo(goods.getLotno());
            updateLocInventoryInputDtoLocNull.setInvAttr(goods.getInvAttr());
            updateLocInventoryInputDtoLocNull.setQaStatus(goods.getQaStatus());
            updateLocInventoryInputDtoLocNull.setUnit(goods.getUnit());
            updateLocInventoryInputDtoLocNull.setQty(goods.getQty());
            updateLocInventoryInputDtoLocNull.setOperator(loginName);
            inventoryService.updateLocInventroy(updateLocInventoryInputDtoLocNull);
            //========================================================================================================
            //扣减SN所在库区库存
            UpdateInventoryInputDto zoneUpdateLoc = new UpdateInventoryInputDto();

            zoneUpdateLoc.setWhsId(goods.getWhsId());
            zoneUpdateLoc.setLocId(tempLocId);
            zoneUpdateLoc.setOwner(goods.getOwner());
            zoneUpdateLoc.setSkuId(goods.getSkuId());
            zoneUpdateLoc.setFactory(goods.getFactory());
            zoneUpdateLoc.setVendorId(goods.getVendorId());
            zoneUpdateLoc.setLotNo(goods.getLotno());
            zoneUpdateLoc.setInvAttr(goods.getInvAttr());
            zoneUpdateLoc.setQaStatus(goods.getQaStatus());
            zoneUpdateLoc.setUnit(goods.getUnit());
            zoneUpdateLoc.setOperator(loginName);
            zoneUpdateLoc.setQty(goods.getQty().negate());
//                zoneUpdateLoc.setZoneId(Integer.parseInt(udf1));

            inventoryService.updateZoneInventory(zoneUpdateLoc);

            //增加空库区库存
            UpdateInventoryInputDto zoneUpdateLocNull = new UpdateInventoryInputDto();
            zoneUpdateLocNull.setWhsId(goods.getWhsId());
            zoneUpdateLocNull.setLocId(null);
            zoneUpdateLocNull.setOwner(goods.getOwner());
            zoneUpdateLocNull.setSkuId(goods.getSkuId());
            zoneUpdateLocNull.setFactory(goods.getFactory());
            zoneUpdateLocNull.setVendorId(goods.getVendorId());
            zoneUpdateLocNull.setLotNo(goods.getLotno());
            zoneUpdateLocNull.setInvAttr(goods.getInvAttr());
            zoneUpdateLocNull.setQaStatus(goods.getQaStatus());
            zoneUpdateLocNull.setUnit(goods.getUnit());
            zoneUpdateLocNull.setOperator(loginName);
            zoneUpdateLocNull.setQty(goods.getQty());
            zoneUpdateLocNull.setZoneId(null);

            inventoryService.updateZoneInventory(zoneUpdateLocNull);
        }
    }

    /**
     *
     * @param picktaskNo
     * @param picktaskLineNo
     * @param gdnNo
     * @param whsId
     * @param servletRequest
     * @param servletResponse
     */
    public List<InvZoneDto> splitGetZone(String picktaskNo, String picktaskLineNo, String gdnNo, Integer whsId, HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        List<InvZoneDto> zoneDtoArrayList = new ArrayList<>();

        PicktaskHeader picktaskHeader = picktaskHeaderService.selectByPickTaskNo(picktaskNo);

        PicktaskDtl picktaskDtl = picktaskDtlService.getDtlByHeadIdAndLineNo(picktaskHeader.getPicktaskHeaderId(), picktaskLineNo);

        GdnDtl gdnDtl = gdnCoreService.getGdnDtlByGdnDtlId(picktaskDtl.getDocDtlId());

        DocPreAloc docPreAloc = docPreAlocService.selectByPk(picktaskDtl.getDocPreAlocId());

        SearchInvRequest searchInvRequest =new SearchInvRequest();
        searchInvRequest.setWhsId(whsId);
        searchInvRequest.setSkuId(gdnDtl.getSkuId());
        searchInvRequest.setOwnerId(docPreAloc.getOwner());
        searchInvRequest.setFactory(docPreAloc.getFactory());
        searchInvRequest.setVendorId(docPreAloc.getVendorId());
        searchInvRequest.setLotno(docPreAloc.getLotno());
        searchInvRequest.setInvAttr(docPreAloc.getInvAttr());
        searchInvRequest.setQaStatus(docPreAloc.getQaStatus());

        List<InvZone> invZones = invZoneService.searchInvZoneByKey(searchInvRequest);
        if (invZones.size() == 0) {
            return zoneDtoArrayList;
        }


        List<Integer> zoneIdList = invZones.stream().filter(s -> s.getZoneId() != null).map(s -> s.getZoneId()).collect(Collectors.toList());

        List<Zone> zoneList = new ArrayList<>();
        if (zoneIdList.size() != 0) {
            zoneList = zoneService.getZoneListByZoneIdList(zoneIdList);
        }

        Map<Integer, List<Zone>> zoneMap = zoneList.stream().collect(Collectors.groupingBy(s -> s.getZoneId()));

        for (InvZone invZone : invZones) {

            InvZoneDto invZoneDto = new InvZoneDto();

            if (zoneMap.containsKey(invZone.getZoneId())) {

                List<Zone> zones = zoneMap.get(invZone.getZoneId());
                Zone zone = zones.get(0);

                invZoneDto.setInvZoneId(invZone.getInvZoneId());
                invZoneDto.setZoneId(zone.getZoneId());
                invZoneDto.setZoneCode(zone.getZoneCode());
                invZoneDto.setZoneName(zone.getZoneName());

                BigDecimal preOutQty = invZone.getPreOutQty();
                BigDecimal holdQty = invZone.getHoldQty();
                BigDecimal preAlocQty = invZone.getPreAlocQty();
                BigDecimal alocQty = invZone.getAlocQty();
                BigDecimal qty = invZone.getQty();

                BigDecimal total = qty.subtract(preOutQty).subtract(holdQty).subtract(preAlocQty).subtract(alocQty);


                invZoneDto.setTotalQty(total);


            } else {

                invZoneDto.setInvZoneId(invZone.getInvZoneId());
                invZoneDto.setZoneId(null);
                invZoneDto.setZoneCode("待上架区");
                invZoneDto.setZoneName("待上架区");

                BigDecimal preOutQty = invZone.getPreOutQty();
                BigDecimal holdQty = invZone.getHoldQty();
                BigDecimal preAlocQty = invZone.getPreAlocQty();
                BigDecimal alocQty = invZone.getAlocQty();
                BigDecimal qty = invZone.getQty();

                BigDecimal total = qty.subtract(preOutQty).subtract(holdQty).subtract(preAlocQty).subtract(alocQty);
                invZoneDto.setTotalQty(total);

            }

            invZoneDto.setInvZoneId(invZone.getInvZoneId());
            zoneDtoArrayList.add(invZoneDto);
        }

        zoneDtoArrayList = zoneDtoArrayList.stream().filter(s -> !(s.getTotalQty().compareTo(BigDecimal.ZERO) == 0)).collect(Collectors.toList());

        return zoneDtoArrayList;
    }

    /**
     * 拆分任务
     * @param picktaskNo
     * @param picktaskLineNo
     * @param gdnNo
     * @param whsId
     * @param qty
     * @param invZoneId
     * @param zoneId
     * @param servletRequest
     * @param servletResponse
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result splitTask(String picktaskNo,
                            String picktaskLineNo,
                            String gdnNo,
                            Integer whsId,
                            Integer qty,
                            Integer invZoneId,
                            Integer zoneId,
                            HttpServletRequest servletRequest,
                            HttpServletResponse servletResponse) {

        if (qty == 0) {
            Shift.fatal(StatusCode.OPERATE_ERR,"不能拆0条");
        }

        PicktaskHeader picktaskHeader = picktaskHeaderService.selectByPickTaskNo(picktaskNo);
        PicktaskDtl picktaskDtl = picktaskDtlService.getDtlByHeadIdAndLineNo(picktaskHeader.getPicktaskHeaderId(), picktaskLineNo);
        GdnDtl gdnDtl = gdnCoreService.getGdnDtlByGdnDtlId(picktaskDtl.getDocDtlId());
        DocPreAloc docPreAloc = docPreAlocService.selectByPk(picktaskDtl.getDocPreAlocId());

        String loginName = HttpSessionUtil.getLoginName(servletRequest);

        //校验结束
        Integer picktaskDtlStatus = picktaskDtl.getStatus();

        int noPickCode = PicktaskStatusEnum.NO_PICK.getCode();
        int pickIngCode = PicktaskStatusEnum.PICK_ING.getCode();

        //当前拣货任务明细状态必须为1(未拣货)、2(拣货中)，否则返回错误信息：当前拣货任务状态[XX]，不可拆分
        if (!(picktaskDtlStatus.equals(noPickCode)||picktaskDtlStatus.equals(pickIngCode))) {
            PicktaskStatusEnum statusEnum = PicktaskStatusEnum.getEnumByCode(picktaskDtlStatus);

            String msg = String.format("当前拣货任务状态[%s]，不可拆分", statusEnum.getEnumName());
            Shift.fatal(StatusCode.OPERATE_ERR, msg);
        }


        BigDecimal waitPickQty = picktaskDtl.getPlanQty().subtract(picktaskDtl.getCmpQty());
        BigDecimal splitQty = new BigDecimal(qty);

        if (splitQty.compareTo(waitPickQty) == 1) {
            //拆分数量大于待拣数量
            String msg = String.format("拆分数量【%s】不可大于当前任务待检数量【%s】", splitQty.toString(), waitPickQty.toString());
            Shift.fatal(StatusCode.OPERATE_ERR, msg);
        }

        InvZone invZone = invZoneService.selectByPk(invZoneId);

        BigDecimal preOutQty = invZone.getPreOutQty();
        BigDecimal holdQty = invZone.getHoldQty();
        BigDecimal preAlocQty = invZone.getPreAlocQty();
        BigDecimal alocQty = invZone.getAlocQty();
        BigDecimal invZoneQty = invZone.getQty();

        BigDecimal leisureQty = invZoneQty.subtract(preOutQty).subtract(holdQty).subtract(preAlocQty).subtract(alocQty);

        if (leisureQty.compareTo(splitQty) == -1) {
            //可用库存数量小于待拣数量
//            所选库区的可用库存数小于拆分数量，请重新选择库区或减少拆分数量。
            String msg = String.format("所选库区的可用库存数【%s】小于拆分数量【%s】，请重新选择库区或减少拆分数量", leisureQty.toString(), splitQty.toString());
            Shift.fatal(StatusCode.OPERATE_ERR, msg);
        }
        //校验结束
        DocPreAloc docPreAlocChange = new DocPreAloc();
        BeanUtils.copyProperties(docPreAloc, docPreAlocChange);
        docPreAlocChange.setDocPreAlocId(null);
        docPreAlocChange.setWhsId(whsId);
        docPreAlocChange.setZoneId(zoneId);
        docPreAlocChange.setPreAlocQty(splitQty);
        String comments =String.format("拆分自预配单[ID:%s]",docPreAloc.getDocPreAlocId());
        docPreAlocChange.setComments(comments);
        docPreAlocChange.setModifyTime(null);
        docPreAlocChange.setModifyUser(null);

        docPreAlocChange.setUdf1(invZone.getInvZoneId().toString());

        docPreAlocChange.setCreateTime(new Date());
        docPreAlocChange.setCreateUser(loginName);


        Integer row = docPreAlocService.addNewOne(docPreAlocChange);
        log.info("成功新增{}条记录",row);

        //TODO 测试代码，测试和投产记得删除
//        int a = 0 / 0;

        DocPreAloc docPreAlocUpdate = new DocPreAloc();
        docPreAlocUpdate.setDocPreAlocId(docPreAloc.getDocPreAlocId());
        docPreAlocUpdate.setModifyUser(loginName);
        docPreAlocUpdate.setModifyTime(new Date());
        comments = String.format("拆分预分配数[%s]到预分配单[ID:%s]", splitQty.toString(), docPreAlocChange.getDocPreAlocId());
        docPreAlocUpdate.setComments(comments);

        Integer rowUpdate = docPreAlocService.updateSplit(docPreAlocUpdate, splitQty.negate());
        log.info("成功更新{}条记录",rowUpdate);

        //新增一笔拣货记录

        PicktaskDtl picktaskDtlNew = new PicktaskDtl();
        BeanUtils.copyProperties(picktaskDtl, picktaskDtlNew);

        picktaskDtlNew.setPicktaskDtlId(null);

        List<PicktaskDtl> picktaskDtlList = picktaskDtlService.getDtlByHeadId(picktaskHeader.getPicktaskHeaderId());

        List<Integer> lineNoList = picktaskDtlList.stream().map(s -> s.getPicktaskLineNo()).collect(Collectors.toList());

        lineNoList = lineNoList.stream().sorted((s,b)->b-s).collect(Collectors.toList());

//        picktaskDtlList = picktaskDtlList.stream().sorted((a, b) -> a.getPicktaskLineNo() - b.getPicktaskLineNo()).collect(Collectors.toList());

        PicktaskDtl picktaskDtlMax = picktaskDtlList.get(0);

        Integer maxLine = lineNoList.get(0);
//        picktaskDtlNew.setPicktaskLineNo(picktaskDtlMax.getPicktaskLineNo() + 1);
        picktaskDtlNew.setPicktaskLineNo(maxLine + 1);

        //设置任务领取人
        String taskReceiver = getTaskReceiver(docPreAloc);
        picktaskDtlNew.setTaskReceiver(taskReceiver);

        picktaskDtlNew.setTaskExecutor(null);
        picktaskDtlNew.setBeginPickTime(null);
        picktaskDtlNew.setEndPickTime(null);

        picktaskDtlNew.setPlanQty(splitQty);
        picktaskDtlNew.setCmpQty(BigDecimal.ZERO);

        comments = String.format("任务拆分自拣货任务[单号/行号:%s/%s]", picktaskHeader.getPicktaskNo(),picktaskDtl.getPicktaskLineNo());
        picktaskDtlNew.setComments(comments);
        picktaskDtlNew.setStatus(PicktaskStatusEnum.NO_PICK.getCode());
        picktaskDtlNew.setCreateTime(new Date());
        picktaskDtlNew.setCreateUser(loginName);
        picktaskDtlNew.setModifyTime(null);
        picktaskDtlNew.setModifyUser(null);
        picktaskDtlNew.setZoneId(zoneId);
        picktaskDtlNew.setDocPreAlocId(docPreAlocChange.getDocPreAlocId());


        Integer rowNewDtl = picktaskDtlService.addOne(picktaskDtlNew);
        log.info("成功新增{}条记录",rowNewDtl);


        //更新原来的
        PicktaskDtl picktaskDtlUpdate = new PicktaskDtl();
        comments = String.format("拆分需求[%s]到拣货任务[任务单号/行号:%s/%s]", splitQty.toString(), picktaskHeader.getPicktaskNo(), picktaskDtlNew.getPicktaskLineNo());
        picktaskDtlUpdate.setComments(comments);
        picktaskDtlUpdate.setPicktaskDtlId(picktaskDtl.getPicktaskDtlId());
        picktaskDtlUpdate.setModifyUser(loginName);
        picktaskDtlUpdate.setModifyTime(new Date());

        Integer rowUpdatePickDtl = picktaskDtlService.updateSplit(picktaskDtlUpdate, splitQty.negate());
        log.info("成功更新{}条记录",rowUpdatePickDtl);

        PicktaskDtl picktaskDtlNewState = picktaskDtlService.selectByPicktaskDtlId(picktaskDtl.getPicktaskDtlId());

        if (picktaskDtlNewState.getPlanQty().equals(picktaskDtlNewState.getCmpQty())) {
            PicktaskDtl picktaskDtlUp = new PicktaskDtl();
            picktaskDtlUp.setPicktaskDtlId(picktaskDtl.getPicktaskDtlId());
            picktaskDtlUp.setModifyTime(new Date());
            picktaskDtlUp.setModifyUser(loginName);
            picktaskDtlUp.setStatus(PicktaskStatusEnum.PICK_ED.getCode());

            Integer rowState = picktaskDtlService.updateByPk(picktaskDtlUp);
            log.info("成功更新{}条记录",rowState);
        }

        Result result = new Result();

        //TODO 测试代码，测试和投产记得删除
//        int a = 0 / 0;
        result.setData("成功拆分一条明细");
        return result;
    }
}